Revert "feat: put new SSH layer behind a feature flag"

This reverts commit fb8d74fc1f.
This commit is contained in:
Harsh Shandilya
2023-12-15 17:34:19 +05:30
parent 424f654be0
commit 4d5b32d98b
9 changed files with 32 additions and 127 deletions

View File

@@ -15,6 +15,7 @@ import androidx.fragment.app.FragmentActivity
import app.passwordstore.R import app.passwordstore.R
import app.passwordstore.data.repo.PasswordRepository import app.passwordstore.data.repo.PasswordRepository
import app.passwordstore.injection.prefs.GitPreferences import app.passwordstore.injection.prefs.GitPreferences
import app.passwordstore.ssh.SSHKeyManager
import app.passwordstore.ui.git.config.GitConfigActivity import app.passwordstore.ui.git.config.GitConfigActivity
import app.passwordstore.ui.git.config.GitServerConfigActivity import app.passwordstore.ui.git.config.GitServerConfigActivity
import app.passwordstore.ui.proxy.ProxySelectorActivity import app.passwordstore.ui.proxy.ProxySelectorActivity
@@ -28,7 +29,6 @@ import app.passwordstore.util.extensions.snackbar
import app.passwordstore.util.extensions.unsafeLazy import app.passwordstore.util.extensions.unsafeLazy
import app.passwordstore.util.settings.GitSettings import app.passwordstore.util.settings.GitSettings
import app.passwordstore.util.settings.PreferenceKeys import app.passwordstore.util.settings.PreferenceKeys
import app.passwordstore.util.ssh.SSHFacade
import com.github.michaelbull.result.onFailure import com.github.michaelbull.result.onFailure
import com.github.michaelbull.result.runCatching import com.github.michaelbull.result.runCatching
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
@@ -44,11 +44,11 @@ import de.Maxr1998.modernpreferences.helpers.switch
class RepositorySettings( class RepositorySettings(
private val activity: FragmentActivity, private val activity: FragmentActivity,
private val sshFacade: SSHFacade, private val sshKeyManager: SSHKeyManager,
) : SettingsProvider { ) : SettingsProvider {
private val generateSshKey = private val generateSshKey =
activity.registerForActivityResult(StartActivityForResult()) { activity.registerForActivityResult(StartActivityForResult()) {
showSshKeyPref?.visible = sshFacade.canShowPublicKey() showSshKeyPref?.visible = sshKeyManager.canShowPublicKey()
} }
private val hiltEntryPoint by unsafeLazy { private val hiltEntryPoint by unsafeLazy {
@@ -113,7 +113,7 @@ class RepositorySettings(
showSshKeyPref = showSshKeyPref =
pref(PreferenceKeys.SSH_SEE_KEY) { pref(PreferenceKeys.SSH_SEE_KEY) {
titleRes = R.string.pref_ssh_see_key_title titleRes = R.string.pref_ssh_see_key_title
visible = PasswordRepository.isGitRepo() && sshFacade.canShowPublicKey() visible = PasswordRepository.isGitRepo() && sshKeyManager.canShowPublicKey()
onClick { onClick {
ShowSshKeyFragment().show(activity.supportFragmentManager, "public_key") ShowSshKeyFragment().show(activity.supportFragmentManager, "public_key")
true true

View File

@@ -11,8 +11,8 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.os.BundleCompat import androidx.core.os.BundleCompat
import app.passwordstore.R import app.passwordstore.R
import app.passwordstore.databinding.ActivityPreferenceRecyclerviewBinding import app.passwordstore.databinding.ActivityPreferenceRecyclerviewBinding
import app.passwordstore.ssh.SSHKeyManager
import app.passwordstore.util.extensions.viewBinding import app.passwordstore.util.extensions.viewBinding
import app.passwordstore.util.ssh.SSHFacade
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import de.Maxr1998.modernpreferences.Preference import de.Maxr1998.modernpreferences.Preference
@@ -24,7 +24,7 @@ import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
class SettingsActivity : AppCompatActivity() { class SettingsActivity : AppCompatActivity() {
@Inject lateinit var sshFacade: SSHFacade @Inject lateinit var sshKeyManager: SSHKeyManager
private lateinit var repositorySettings: RepositorySettings private lateinit var repositorySettings: RepositorySettings
private val miscSettings = MiscSettings(this) private val miscSettings = MiscSettings(this)
private val autofillSettings = AutofillSettings(this) private val autofillSettings = AutofillSettings(this)
@@ -40,7 +40,7 @@ class SettingsActivity : AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(binding.root) setContentView(binding.root)
Preference.Config.dialogBuilderFactory = { context -> MaterialAlertDialogBuilder(context) } Preference.Config.dialogBuilderFactory = { context -> MaterialAlertDialogBuilder(context) }
repositorySettings = RepositorySettings(this, sshFacade) repositorySettings = RepositorySettings(this, sshKeyManager)
val screen = val screen =
screen(this) { screen(this) {
subScreen { subScreen {

View File

@@ -9,7 +9,7 @@ import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import app.passwordstore.R import app.passwordstore.R
import app.passwordstore.util.ssh.SSHFacade import app.passwordstore.ssh.SSHKeyManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject import javax.inject.Inject
@@ -17,11 +17,11 @@ import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
class ShowSshKeyFragment : DialogFragment() { class ShowSshKeyFragment : DialogFragment() {
@Inject lateinit var sshFacade: SSHFacade @Inject lateinit var sshKeyManager: SSHKeyManager
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val activity = requireActivity() val activity = requireActivity()
val publicKey = sshFacade.publicKey() val publicKey = sshKeyManager.publicKey()
return MaterialAlertDialogBuilder(requireActivity()).run { return MaterialAlertDialogBuilder(requireActivity()).run {
setMessage(getString(R.string.ssh_keygen_message, publicKey)) setMessage(getString(R.string.ssh_keygen_message, publicKey))
setTitle(R.string.your_public_key) setTitle(R.string.your_public_key)

View File

@@ -18,12 +18,12 @@ import app.passwordstore.R
import app.passwordstore.databinding.ActivitySshKeygenBinding import app.passwordstore.databinding.ActivitySshKeygenBinding
import app.passwordstore.injection.prefs.GitPreferences import app.passwordstore.injection.prefs.GitPreferences
import app.passwordstore.ssh.SSHKeyAlgorithm import app.passwordstore.ssh.SSHKeyAlgorithm
import app.passwordstore.ssh.SSHKeyManager
import app.passwordstore.util.auth.BiometricAuthenticator import app.passwordstore.util.auth.BiometricAuthenticator
import app.passwordstore.util.auth.BiometricAuthenticator.Result import app.passwordstore.util.auth.BiometricAuthenticator.Result
import app.passwordstore.util.coroutines.DispatcherProvider import app.passwordstore.util.coroutines.DispatcherProvider
import app.passwordstore.util.extensions.keyguardManager import app.passwordstore.util.extensions.keyguardManager
import app.passwordstore.util.extensions.viewBinding import app.passwordstore.util.extensions.viewBinding
import app.passwordstore.util.ssh.SSHFacade
import com.github.michaelbull.result.fold import com.github.michaelbull.result.fold
import com.github.michaelbull.result.runCatching import com.github.michaelbull.result.runCatching
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
@@ -40,7 +40,7 @@ class SshKeyGenActivity : AppCompatActivity() {
private var sshKeyAlgorithm = SSHKeyAlgorithm.ECDSA private var sshKeyAlgorithm = SSHKeyAlgorithm.ECDSA
private val binding by viewBinding(ActivitySshKeygenBinding::inflate) private val binding by viewBinding(ActivitySshKeygenBinding::inflate)
@GitPreferences @Inject lateinit var gitPrefs: SharedPreferences @GitPreferences @Inject lateinit var gitPrefs: SharedPreferences
@Inject lateinit var sshFacade: SSHFacade @Inject lateinit var sshKeyManager: SSHKeyManager
@Inject lateinit var dispatcherProvider: DispatcherProvider @Inject lateinit var dispatcherProvider: DispatcherProvider
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@@ -49,7 +49,7 @@ class SshKeyGenActivity : AppCompatActivity() {
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
with(binding) { with(binding) {
generate.setOnClickListener { generate.setOnClickListener {
if (sshFacade.keyExists()) { if (sshKeyManager.keyExists()) {
MaterialAlertDialogBuilder(this@SshKeyGenActivity).run { MaterialAlertDialogBuilder(this@SshKeyGenActivity).run {
setTitle(R.string.ssh_keygen_existing_title) setTitle(R.string.ssh_keygen_existing_title)
setMessage(R.string.ssh_keygen_existing_message) setMessage(R.string.ssh_keygen_existing_message)
@@ -127,7 +127,7 @@ class SshKeyGenActivity : AppCompatActivity() {
if (result !is Result.Success) if (result !is Result.Success)
throw UserNotAuthenticatedException(getString(R.string.biometric_auth_generic_failure)) throw UserNotAuthenticatedException(getString(R.string.biometric_auth_generic_failure))
} }
sshFacade.generateKey(sshKeyAlgorithm, requireAuthentication) sshKeyManager.generateKey(sshKeyAlgorithm, requireAuthentication)
} }
} }
// Check if we still need this // Check if we still need this

View File

@@ -12,7 +12,7 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import app.passwordstore.R import app.passwordstore.R
import app.passwordstore.util.ssh.SSHFacade import app.passwordstore.ssh.SSHKeyManager
import com.github.michaelbull.result.onFailure import com.github.michaelbull.result.onFailure
import com.github.michaelbull.result.runCatching import com.github.michaelbull.result.runCatching
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
@@ -23,7 +23,7 @@ import kotlinx.coroutines.launch
@AndroidEntryPoint @AndroidEntryPoint
class SshKeyImportActivity : AppCompatActivity() { class SshKeyImportActivity : AppCompatActivity() {
@Inject lateinit var sshFacade: SSHFacade @Inject lateinit var sshKeyManager: SSHKeyManager
private val sshKeyImportAction = private val sshKeyImportAction =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri: Uri? -> registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri: Uri? ->
@@ -33,7 +33,7 @@ class SshKeyImportActivity : AppCompatActivity() {
} }
runCatching { runCatching {
lifecycleScope.launch { lifecycleScope.launch {
sshFacade.importKey(uri) sshKeyManager.importKey(uri)
Toast.makeText( Toast.makeText(
this@SshKeyImportActivity, this@SshKeyImportActivity,
resources.getString(R.string.ssh_key_success_dialog_title), resources.getString(R.string.ssh_key_success_dialog_title),
@@ -55,7 +55,7 @@ class SshKeyImportActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
if (sshFacade.keyExists()) { if (sshKeyManager.keyExists()) {
MaterialAlertDialogBuilder(this@SshKeyImportActivity).run { MaterialAlertDialogBuilder(this@SshKeyImportActivity).run {
setTitle(R.string.ssh_keygen_existing_title) setTitle(R.string.ssh_keygen_existing_title)
setMessage(R.string.ssh_keygen_existing_message) setMessage(R.string.ssh_keygen_existing_message)

View File

@@ -13,9 +13,6 @@ enum class Feature(
val configKey: String, val configKey: String,
) { ) {
/** Opt into the new SSH layer implemented as a freestanding module. */
EnableNewSSHLayer(false, "enable_new_ssh"),
/** Opt into a cache layer for PGP passphrases. */ /** Opt into a cache layer for PGP passphrases. */
EnablePGPPassphraseCache(false, "enable_gpg_passphrase_cache"), EnablePGPPassphraseCache(false, "enable_gpg_passphrase_cache"),
} }

View File

@@ -9,6 +9,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import app.passwordstore.R import app.passwordstore.R
import app.passwordstore.data.repo.PasswordRepository import app.passwordstore.data.repo.PasswordRepository
import app.passwordstore.ssh.SSHKeyManager
import app.passwordstore.ui.sshkeygen.SshKeyGenActivity import app.passwordstore.ui.sshkeygen.SshKeyGenActivity
import app.passwordstore.ui.sshkeygen.SshKeyImportActivity import app.passwordstore.ui.sshkeygen.SshKeyImportActivity
import app.passwordstore.util.auth.BiometricAuthenticator import app.passwordstore.util.auth.BiometricAuthenticator
@@ -22,7 +23,6 @@ import app.passwordstore.util.git.GitCommandExecutor
import app.passwordstore.util.git.sshj.SshAuthMethod import app.passwordstore.util.git.sshj.SshAuthMethod
import app.passwordstore.util.git.sshj.SshjSessionFactory import app.passwordstore.util.git.sshj.SshjSessionFactory
import app.passwordstore.util.settings.AuthMode import app.passwordstore.util.settings.AuthMode
import app.passwordstore.util.ssh.SSHFacade
import com.github.michaelbull.result.Err import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.Result import com.github.michaelbull.result.Result
@@ -71,7 +71,7 @@ abstract class GitOperation(protected val callingActivity: FragmentActivity) {
callingActivity.applicationContext, callingActivity.applicationContext,
GitOperationEntryPoint::class.java GitOperationEntryPoint::class.java
) )
private val sshFacade = hiltEntryPoint.sshFacade() private val sshKeyManager = hiltEntryPoint.sshKeyManager()
protected val repository = PasswordRepository.repository!! protected val repository = PasswordRepository.repository!!
protected val git = Git(repository) protected val git = Git(repository)
private val authActivity private val authActivity
@@ -121,8 +121,7 @@ abstract class GitOperation(protected val callingActivity: FragmentActivity) {
authMethod: SshAuthMethod, authMethod: SshAuthMethod,
credentialsProvider: CredentialsProvider? = null credentialsProvider: CredentialsProvider? = null
) { ) {
sshSessionFactory = sshSessionFactory = SshjSessionFactory(authMethod, hostKeyFile, sshKeyManager, hiltEntryPoint.dispatcherProvider())
SshjSessionFactory(authMethod, hostKeyFile, sshFacade, hiltEntryPoint.dispatcherProvider())
commands.filterIsInstance<TransportCommand<*, *>>().forEach { command -> commands.filterIsInstance<TransportCommand<*, *>>().forEach { command ->
command.setTransportConfigCallback { transport: Transport -> command.setTransportConfigCallback { transport: Transport ->
(transport as? SshTransport)?.sshSessionFactory = sshSessionFactory (transport as? SshTransport)?.sshSessionFactory = sshSessionFactory
@@ -170,8 +169,8 @@ abstract class GitOperation(protected val callingActivity: FragmentActivity) {
suspend fun executeAfterAuthentication(authMode: AuthMode): Result<Unit, Throwable> { suspend fun executeAfterAuthentication(authMode: AuthMode): Result<Unit, Throwable> {
when (authMode) { when (authMode) {
AuthMode.SshKey -> AuthMode.SshKey ->
if (sshFacade.keyExists()) { if (sshKeyManager.keyExists()) {
if (sshFacade.needsAuthentication()) { if (sshKeyManager.needsAuthentication()) {
val result = val result =
withContext(hiltEntryPoint.dispatcherProvider().main()) { withContext(hiltEntryPoint.dispatcherProvider().main()) {
suspendCoroutine { cont -> suspendCoroutine { cont ->
@@ -248,7 +247,7 @@ abstract class GitOperation(protected val callingActivity: FragmentActivity) {
@EntryPoint @EntryPoint
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)
interface GitOperationEntryPoint { interface GitOperationEntryPoint {
fun sshFacade(): SSHFacade fun sshKeyManager(): SSHKeyManager
fun dispatcherProvider(): DispatcherProvider fun dispatcherProvider(): DispatcherProvider
} }

View File

@@ -6,10 +6,10 @@ package app.passwordstore.util.git.sshj
import android.util.Base64 import android.util.Base64
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import app.passwordstore.ssh.SSHKeyManager
import app.passwordstore.util.coroutines.DispatcherProvider import app.passwordstore.util.coroutines.DispatcherProvider
import app.passwordstore.util.git.operation.CredentialFinder import app.passwordstore.util.git.operation.CredentialFinder
import app.passwordstore.util.settings.AuthMode import app.passwordstore.util.settings.AuthMode
import app.passwordstore.util.ssh.SSHFacade
import com.github.michaelbull.result.getOrElse import com.github.michaelbull.result.getOrElse
import com.github.michaelbull.result.runCatching import com.github.michaelbull.result.runCatching
import java.io.File import java.io.File
@@ -71,7 +71,7 @@ abstract class InteractivePasswordFinder(private val dispatcherProvider: Dispatc
class SshjSessionFactory( class SshjSessionFactory(
private val authMethod: SshAuthMethod, private val authMethod: SshAuthMethod,
private val hostKeyFile: File, private val hostKeyFile: File,
private val sshFacade: SSHFacade, private val sshKeyManager: SSHKeyManager,
private val dispatcherProvider: DispatcherProvider, private val dispatcherProvider: DispatcherProvider,
) : SshSessionFactory() { ) : SshSessionFactory() {
@@ -84,12 +84,10 @@ class SshjSessionFactory(
tms: Int tms: Int
): RemoteSession { ): RemoteSession {
return currentSession return currentSession
?: SshjSession(uri, uri.user, authMethod, hostKeyFile, sshFacade, dispatcherProvider) ?: SshjSession(uri, uri.user, authMethod, hostKeyFile, dispatcherProvider, sshKeyManager).connect().also {
.connect() logcat { "New SSH connection created" }
.also { currentSession = it
logcat { "New SSH connection created" } }
currentSession = it
}
} }
fun close() { fun close() {
@@ -130,8 +128,8 @@ private class SshjSession(
private val username: String, private val username: String,
private val authMethod: SshAuthMethod, private val authMethod: SshAuthMethod,
private val hostKeyFile: File, private val hostKeyFile: File,
private val sshFacade: SSHFacade,
private val dispatcherProvider: DispatcherProvider, private val dispatcherProvider: DispatcherProvider,
private val sshKeyManager: SSHKeyManager,
) : RemoteSession { ) : RemoteSession {
private lateinit var ssh: SSHClient private lateinit var ssh: SSHClient
@@ -167,10 +165,7 @@ private class SshjSession(
is SshAuthMethod.SshKey -> { is SshAuthMethod.SshKey -> {
val pubkeyAuth = val pubkeyAuth =
AuthPublickey( AuthPublickey(
sshFacade.keyProvider( sshKeyManager.keyProvider(ssh, CredentialFinder(authMethod.activity, AuthMode.SshKey, dispatcherProvider))
ssh,
CredentialFinder(authMethod.activity, AuthMode.SshKey, dispatcherProvider)
)
) )
ssh.auth(username, pubkeyAuth, passwordAuth) ssh.auth(username, pubkeyAuth, passwordAuth)
} }

View File

@@ -1,86 +0,0 @@
package app.passwordstore.util.ssh
import android.net.Uri
import app.passwordstore.ssh.SSHKeyAlgorithm
import app.passwordstore.ssh.SSHKeyManager
import app.passwordstore.util.features.Feature
import app.passwordstore.util.features.Features
import app.passwordstore.util.git.operation.CredentialFinder
import app.passwordstore.util.git.sshj.SshKey
import javax.inject.Inject
import net.schmizz.sshj.SSHClient
import net.schmizz.sshj.userauth.keyprovider.KeyProvider
/** A wrapper around [SshKey] and [SSHKeyManager] to allow switching between them at runtime. */
class SSHFacade
@Inject
constructor(
private val features: Features,
private val sshKeyManager: SSHKeyManager,
) {
private val useNewSSH
get() = features.isEnabled(Feature.EnableNewSSHLayer)
fun canShowPublicKey(): Boolean {
return if (useNewSSH) {
sshKeyManager.canShowPublicKey()
} else {
SshKey.canShowSshPublicKey
}
}
fun publicKey(): String? {
return if (useNewSSH) {
sshKeyManager.publicKey()
} else {
SshKey.sshPublicKey
}
}
fun keyExists(): Boolean {
return if (useNewSSH) {
sshKeyManager.keyExists()
} else {
SshKey.exists
}
}
suspend fun generateKey(keyAlgorithm: SSHKeyAlgorithm, requireAuthentication: Boolean) {
if (useNewSSH) {
sshKeyManager.generateKey(keyAlgorithm, requireAuthentication)
} else {
when (keyAlgorithm) {
SSHKeyAlgorithm.RSA ->
SshKey.generateKeystoreNativeKey(SshKey.Algorithm.Rsa, requireAuthentication)
SSHKeyAlgorithm.ECDSA ->
SshKey.generateKeystoreNativeKey(SshKey.Algorithm.Ecdsa, requireAuthentication)
SSHKeyAlgorithm.ED25519 -> SshKey.generateKeystoreWrappedEd25519Key(requireAuthentication)
}
}
}
suspend fun importKey(uri: Uri) {
if (useNewSSH) {
sshKeyManager.importKey(uri)
} else {
SshKey.import(uri)
}
}
fun needsAuthentication(): Boolean {
return if (useNewSSH) {
sshKeyManager.needsAuthentication()
} else {
SshKey.mustAuthenticate
}
}
fun keyProvider(client: SSHClient, credentialFinder: CredentialFinder): KeyProvider? {
return if (useNewSSH) {
sshKeyManager.keyProvider(client, credentialFinder)
} else {
SshKey.provide(client, credentialFinder)
}
}
}