aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/main/java/com/zeapo/pwdstore/git/operation
diff options
context:
space:
mode:
authorFabian Henneke <FabianHenneke@users.noreply.github.com>2020-09-03 10:48:14 +0200
committerGitHub <noreply@github.com>2020-09-03 14:18:14 +0530
commit3840f43fa0026b06b15913379761c1bf6b6a1c68 (patch)
tree41a5d47c3ed3ef06430d2d045876301d4db416ad /app/src/main/java/com/zeapo/pwdstore/git/operation
parent258ccc601605f3112d9310043b9a34bf6ca3c9e0 (diff)
downloadHardenedPass-3840f43fa0026b06b15913379761c1bf6b6a1c68.tar.gz
HardenedPass-3840f43fa0026b06b15913379761c1bf6b6a1c68.zip
Refactor Git operations and auth (#1066)
Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
Diffstat (limited to 'app/src/main/java/com/zeapo/pwdstore/git/operation')
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/operation/BreakOutOfDetached.kt30
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/operation/CloneOperation.kt20
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/operation/GitOperation.kt128
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/operation/PullOperation.kt17
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/operation/PushOperation.kt18
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/operation/ResetToRemoteOperation.kt14
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/operation/SyncOperation.kt14
7 files changed, 71 insertions, 170 deletions
diff --git a/app/src/main/java/com/zeapo/pwdstore/git/operation/BreakOutOfDetached.kt b/app/src/main/java/com/zeapo/pwdstore/git/operation/BreakOutOfDetached.kt
index b46b66cf..6c7fee3a 100644
--- a/app/src/main/java/com/zeapo/pwdstore/git/operation/BreakOutOfDetached.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/git/operation/BreakOutOfDetached.kt
@@ -7,46 +7,30 @@ package com.zeapo.pwdstore.git.operation
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.zeapo.pwdstore.R
-import com.zeapo.pwdstore.git.GitCommandExecutor
-import java.io.File
import org.eclipse.jgit.api.RebaseCommand
-class BreakOutOfDetached(fileDir: File, callingActivity: AppCompatActivity) : GitOperation(fileDir, callingActivity) {
-
- private val branchName = "conflicting-$remoteBranch-${System.currentTimeMillis()}"
+class BreakOutOfDetached(callingActivity: AppCompatActivity) : GitOperation(callingActivity) {
override val commands = arrayOf(
// abort the rebase
git.rebase().setOperation(RebaseCommand.Operation.ABORT),
// git checkout -b conflict-branch
- git.checkout().setCreateBranch(true).setName(branchName),
+ git.checkout().setCreateBranch(true).setName("conflicting-$remoteBranch-${System.currentTimeMillis()}"),
// push the changes
git.push().setRemote("origin"),
// switch back to ${gitBranch}
git.checkout().setName(remoteBranch),
)
- override suspend fun execute() {
- if (!git.repository.repositoryState.isRebasing) {
- MaterialAlertDialogBuilder(callingActivity)
- .setTitle(callingActivity.resources.getString(R.string.git_abort_and_push_title))
- .setMessage("The repository is not rebasing, no need to push to another branch")
- .setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ ->
- callingActivity.finish()
- }.show()
- return
- }
- GitCommandExecutor(callingActivity, this).execute()
- }
-
- override fun onSuccess() {
+ override fun preExecute() = if (!git.repository.repositoryState.isRebasing) {
MaterialAlertDialogBuilder(callingActivity)
.setTitle(callingActivity.resources.getString(R.string.git_abort_and_push_title))
- .setMessage("There was a conflict when trying to rebase. " +
- "Your local $remoteBranch branch was pushed to another branch named conflicting-$remoteBranch-....\n" +
- "Use this branch to resolve conflict on your computer")
+ .setMessage(callingActivity.resources.getString(R.string.git_break_out_of_detached_unneeded))
.setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ ->
callingActivity.finish()
}.show()
+ false
+ } else {
+ true
}
}
diff --git a/app/src/main/java/com/zeapo/pwdstore/git/operation/CloneOperation.kt b/app/src/main/java/com/zeapo/pwdstore/git/operation/CloneOperation.kt
index a032a669..256437a8 100644
--- a/app/src/main/java/com/zeapo/pwdstore/git/operation/CloneOperation.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/git/operation/CloneOperation.kt
@@ -5,34 +5,18 @@
package com.zeapo.pwdstore.git.operation
import androidx.appcompat.app.AppCompatActivity
-import com.zeapo.pwdstore.git.GitCommandExecutor
-import java.io.File
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.api.GitCommand
/**
* Creates a new clone operation
*
- * @param fileDir the git working tree directory
* @param uri URL to clone the repository from
* @param callingActivity the calling activity
*/
-class CloneOperation(fileDir: File, uri: String, callingActivity: AppCompatActivity) : GitOperation(fileDir, callingActivity) {
+class CloneOperation(callingActivity: AppCompatActivity, uri: String) : GitOperation(callingActivity) {
override val commands: Array<GitCommand<out Any>> = arrayOf(
- Git.cloneRepository().setBranch(remoteBranch).setDirectory(repository?.workTree).setURI(uri),
+ Git.cloneRepository().setBranch(remoteBranch).setDirectory(repository.workTree).setURI(uri),
)
-
- override suspend fun execute() {
- GitCommandExecutor(callingActivity, this, finishActivityOnEnd = false).execute()
- }
-
- override fun onSuccess() {
- callingActivity.finish()
- }
-
- override fun onError(err: Exception) {
- finishFromErrorDialog = false
- super.onError(err)
- }
}
diff --git a/app/src/main/java/com/zeapo/pwdstore/git/operation/GitOperation.kt b/app/src/main/java/com/zeapo/pwdstore/git/operation/GitOperation.kt
index 1b47d79b..2a72005e 100644
--- a/app/src/main/java/com/zeapo/pwdstore/git/operation/GitOperation.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/git/operation/GitOperation.kt
@@ -6,30 +6,27 @@ package com.zeapo.pwdstore.git.operation
import android.content.Intent
import android.widget.Toast
-import androidx.annotation.CallSuper
-import androidx.core.content.edit
import androidx.fragment.app.FragmentActivity
-import com.github.ajalt.timberkt.Timber.d
+import com.github.michaelbull.result.Err
+import com.github.michaelbull.result.Ok
+import com.github.michaelbull.result.Result
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.zeapo.pwdstore.R
import com.zeapo.pwdstore.UserPreference
-import com.zeapo.pwdstore.git.ErrorMessages
+import com.zeapo.pwdstore.git.GitCommandExecutor
import com.zeapo.pwdstore.git.config.AuthMode
import com.zeapo.pwdstore.git.config.GitSettings
-import com.zeapo.pwdstore.git.sshj.InteractivePasswordFinder
import com.zeapo.pwdstore.git.sshj.SshAuthData
import com.zeapo.pwdstore.git.sshj.SshKey
import com.zeapo.pwdstore.git.sshj.SshjSessionFactory
import com.zeapo.pwdstore.utils.BiometricAuthenticator
import com.zeapo.pwdstore.utils.PasswordRepository
-import com.zeapo.pwdstore.utils.PreferenceKeys
-import com.zeapo.pwdstore.utils.getEncryptedPrefs
-import com.zeapo.pwdstore.utils.sharedPrefs
-import java.io.File
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
+import net.schmizz.sshj.common.DisconnectReason
+import net.schmizz.sshj.common.SSHException
import net.schmizz.sshj.userauth.password.PasswordFinder
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.api.GitCommand
@@ -37,24 +34,22 @@ import org.eclipse.jgit.api.TransportCommand
import org.eclipse.jgit.errors.UnsupportedCredentialItem
import org.eclipse.jgit.transport.CredentialItem
import org.eclipse.jgit.transport.CredentialsProvider
-import org.eclipse.jgit.transport.SshSessionFactory
+import org.eclipse.jgit.transport.SshTransport
+import org.eclipse.jgit.transport.Transport
import org.eclipse.jgit.transport.URIish
-const val ANDROID_KEYSTORE_ALIAS_SSH_KEY = "ssh_key"
-
/**
* Creates a new git operation
*
- * @param gitDir the git working tree directory
* @param callingActivity the calling activity
*/
-abstract class GitOperation(gitDir: File, internal val callingActivity: FragmentActivity) {
+abstract class GitOperation(protected val callingActivity: FragmentActivity) {
abstract val commands: Array<GitCommand<out Any>>
- private var provider: CredentialsProvider? = null
private val hostKeyFile = callingActivity.filesDir.resolve(".host_key")
- protected var finishFromErrorDialog = true
- protected val repository = PasswordRepository.getRepository(gitDir)
+ private var sshSessionFactory: SshjSessionFactory? = null
+
+ protected val repository = PasswordRepository.getRepository(null)!!
protected val git = Git(repository)
protected val remoteBranch = GitSettings.branch
@@ -90,27 +85,6 @@ abstract class GitOperation(gitDir: File, internal val callingActivity: Fragment
}
}
- private fun withPasswordAuthentication(passwordFinder: InteractivePasswordFinder): GitOperation {
- val sessionFactory = SshjSessionFactory(SshAuthData.Password(passwordFinder), hostKeyFile)
- SshSessionFactory.setInstance(sessionFactory)
- this.provider = HttpsCredentialsProvider(passwordFinder)
- return this
- }
-
- private fun withSshKeyAuthentication(passphraseFinder: InteractivePasswordFinder): GitOperation {
- val sessionFactory = SshjSessionFactory(SshAuthData.SshKey(passphraseFinder), hostKeyFile)
- SshSessionFactory.setInstance(sessionFactory)
- this.provider = null
- return this
- }
-
- private fun withOpenKeychainAuthentication(activity: FragmentActivity): GitOperation {
- val sessionFactory = SshjSessionFactory(SshAuthData.OpenKeychain(activity), hostKeyFile)
- SshSessionFactory.setInstance(sessionFactory)
- this.provider = null
- return this
- }
-
private fun getSshKey(make: Boolean) {
try {
// Ask the UserPreference to provide us with the ssh-key
@@ -124,16 +98,30 @@ abstract class GitOperation(gitDir: File, internal val callingActivity: Fragment
}
}
- fun setCredentialProvider() {
- provider?.let { credentialsProvider ->
- commands.filterIsInstance<TransportCommand<*, *>>().forEach { it.setCredentialsProvider(credentialsProvider) }
+ private fun registerAuthProviders(authData: SshAuthData, credentialsProvider: CredentialsProvider? = null) {
+ sshSessionFactory = SshjSessionFactory(authData, hostKeyFile)
+ commands.filterIsInstance<TransportCommand<*, *>>().forEach { command ->
+ command.setTransportConfigCallback { transport: Transport ->
+ (transport as? SshTransport)?.sshSessionFactory = sshSessionFactory
+ credentialsProvider?.let { transport.credentialsProvider = it }
+ }
}
}
/**
- * Executes the GitCommand in an async task
+ * Executes the GitCommand in an async task.
*/
- abstract suspend fun execute()
+ suspend fun execute(): Result<Unit, Throwable> {
+ if (!preExecute()) {
+ return Ok(Unit)
+ }
+ val operationResult = GitCommandExecutor(
+ callingActivity,
+ this,
+ ).execute()
+ postExecute()
+ return operationResult
+ }
private fun onMissingSshKeyFile() {
MaterialAlertDialogBuilder(callingActivity)
@@ -151,9 +139,7 @@ abstract class GitOperation(gitDir: File, internal val callingActivity: Fragment
}.show()
}
- suspend fun executeAfterAuthentication(
- authMode: AuthMode,
- ) {
+ suspend fun executeAfterAuthentication(authMode: AuthMode): Result<Unit, Throwable> {
when (authMode) {
AuthMode.SshKey -> if (SshKey.exists) {
if (SshKey.mustAuthenticate) {
@@ -167,9 +153,12 @@ abstract class GitOperation(gitDir: File, internal val callingActivity: Fragment
}
when (result) {
is BiometricAuthenticator.Result.Success -> {
- withSshKeyAuthentication(CredentialFinder(callingActivity, authMode)).execute()
+ registerAuthProviders(
+ SshAuthData.SshKey(CredentialFinder(callingActivity, AuthMode.SshKey)))
+ }
+ is BiometricAuthenticator.Result.Cancelled -> {
+ return Err(SSHException(DisconnectReason.AUTH_CANCELLED_BY_USER))
}
- is BiometricAuthenticator.Result.Cancelled -> callingActivity.finish()
is BiometricAuthenticator.Result.Failure -> {
throw IllegalStateException("Biometric authentication failures should be ignored")
}
@@ -183,42 +172,37 @@ abstract class GitOperation(gitDir: File, internal val callingActivity: Fragment
}
}
} else {
- withSshKeyAuthentication(CredentialFinder(callingActivity, authMode)).execute()
+ registerAuthProviders(SshAuthData.SshKey(CredentialFinder(callingActivity, AuthMode.SshKey)))
}
} else {
onMissingSshKeyFile()
}
- AuthMode.OpenKeychain -> withOpenKeychainAuthentication(callingActivity).execute()
- AuthMode.Password -> withPasswordAuthentication(
- CredentialFinder(callingActivity, authMode)).execute()
- AuthMode.None -> execute()
+ AuthMode.OpenKeychain -> registerAuthProviders(SshAuthData.OpenKeychain(callingActivity))
+ AuthMode.Password -> {
+ val credentialFinder = CredentialFinder(callingActivity, AuthMode.Password)
+ val httpsCredentialProvider = HttpsCredentialsProvider(credentialFinder)
+ registerAuthProviders(
+ SshAuthData.Password(CredentialFinder(callingActivity, AuthMode.Password)),
+ httpsCredentialProvider)
+ }
+ AuthMode.None -> {
+ }
}
+ return execute()
}
/**
- * Action to execute on error
+ * Called before execution of the Git operation.
+ * Return false to cancel.
*/
- @CallSuper
- open fun onError(err: Exception) {
- // Clear various auth related fields on failure
- callingActivity.getEncryptedPrefs("git_operation").edit {
- remove(PreferenceKeys.HTTPS_PASSWORD)
+ open fun preExecute() = true
+
+ private suspend fun postExecute() {
+ withContext(Dispatchers.IO) {
+ sshSessionFactory?.close()
}
- callingActivity.sharedPrefs.edit { remove(PreferenceKeys.SSH_OPENKEYSTORE_KEYID) }
- d(err)
- MaterialAlertDialogBuilder(callingActivity)
- .setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title))
- .setMessage(ErrorMessages[err])
- .setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ ->
- if (finishFromErrorDialog) callingActivity.finish()
- }.show()
}
- /**
- * Action to execute on success
- */
- open fun onSuccess() {}
-
companion object {
const val GET_SSH_KEY_FROM_CLONE = 201
diff --git a/app/src/main/java/com/zeapo/pwdstore/git/operation/PullOperation.kt b/app/src/main/java/com/zeapo/pwdstore/git/operation/PullOperation.kt
index d31b2aa4..d4ce6d01 100644
--- a/app/src/main/java/com/zeapo/pwdstore/git/operation/PullOperation.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/git/operation/PullOperation.kt
@@ -5,24 +5,11 @@
package com.zeapo.pwdstore.git.operation
import androidx.appcompat.app.AppCompatActivity
-import com.zeapo.pwdstore.git.GitCommandExecutor
-import java.io.File
-import org.eclipse.jgit.api.Git
import org.eclipse.jgit.api.GitCommand
-/**
- * Creates a new git operation
- *
- * @param fileDir the git working tree directory
- * @param callingActivity the calling activity
- */
-class PullOperation(fileDir: File, callingActivity: AppCompatActivity) : GitOperation(fileDir, callingActivity) {
+class PullOperation(callingActivity: AppCompatActivity) : GitOperation(callingActivity) {
override val commands: Array<GitCommand<out Any>> = arrayOf(
- Git(repository).pull().setRebase(true).setRemote("origin"),
+ git.pull().setRebase(true).setRemote("origin"),
)
-
- override suspend fun execute() {
- GitCommandExecutor(callingActivity, this).execute()
- }
}
diff --git a/app/src/main/java/com/zeapo/pwdstore/git/operation/PushOperation.kt b/app/src/main/java/com/zeapo/pwdstore/git/operation/PushOperation.kt
index 33b20a06..d3bee209 100644
--- a/app/src/main/java/com/zeapo/pwdstore/git/operation/PushOperation.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/git/operation/PushOperation.kt
@@ -5,25 +5,11 @@
package com.zeapo.pwdstore.git.operation
import androidx.appcompat.app.AppCompatActivity
-import com.zeapo.pwdstore.git.GitCommandExecutor
-import java.io.File
-import org.eclipse.jgit.api.Git
import org.eclipse.jgit.api.GitCommand
-/**
- * Creates a new git operation
- *
- * @param fileDir the git working tree directory
- * @param callingActivity the calling activity
- */
-class PushOperation(fileDir: File, callingActivity: AppCompatActivity) : GitOperation(fileDir, callingActivity) {
+class PushOperation(callingActivity: AppCompatActivity) : GitOperation(callingActivity) {
override val commands: Array<GitCommand<out Any>> = arrayOf(
- Git(repository).push().setPushAll().setRemote("origin"),
+ git.push().setPushAll().setRemote("origin"),
)
-
- override suspend fun execute() {
- setCredentialProvider()
- GitCommandExecutor(callingActivity, this).execute()
- }
}
diff --git a/app/src/main/java/com/zeapo/pwdstore/git/operation/ResetToRemoteOperation.kt b/app/src/main/java/com/zeapo/pwdstore/git/operation/ResetToRemoteOperation.kt
index 0d0dc019..2df7a2b4 100644
--- a/app/src/main/java/com/zeapo/pwdstore/git/operation/ResetToRemoteOperation.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/git/operation/ResetToRemoteOperation.kt
@@ -5,17 +5,9 @@
package com.zeapo.pwdstore.git.operation
import androidx.appcompat.app.AppCompatActivity
-import com.zeapo.pwdstore.git.GitCommandExecutor
-import java.io.File
import org.eclipse.jgit.api.ResetCommand
-/**
- * Creates a new git operation
- *
- * @param fileDir the git working tree directory
- * @param callingActivity the calling activity
- */
-class ResetToRemoteOperation(fileDir: File, callingActivity: AppCompatActivity) : GitOperation(fileDir, callingActivity) {
+class ResetToRemoteOperation(callingActivity: AppCompatActivity) : GitOperation(callingActivity) {
override val commands = arrayOf(
// Stage all files
@@ -28,8 +20,4 @@ class ResetToRemoteOperation(fileDir: File, callingActivity: AppCompatActivity)
// branches from 'master' to anything else.
git.branchCreate().setName(remoteBranch).setForce(true),
)
-
- override suspend fun execute() {
- GitCommandExecutor(callingActivity, this).execute()
- }
}
diff --git a/app/src/main/java/com/zeapo/pwdstore/git/operation/SyncOperation.kt b/app/src/main/java/com/zeapo/pwdstore/git/operation/SyncOperation.kt
index 35b12810..a0a80fe3 100644
--- a/app/src/main/java/com/zeapo/pwdstore/git/operation/SyncOperation.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/git/operation/SyncOperation.kt
@@ -5,16 +5,8 @@
package com.zeapo.pwdstore.git.operation
import androidx.appcompat.app.AppCompatActivity
-import com.zeapo.pwdstore.git.GitCommandExecutor
-import java.io.File
-/**
- * Creates a new git operation
- *
- * @param fileDir the git working tree directory
- * @param callingActivity the calling activity
- */
-class SyncOperation(fileDir: File, callingActivity: AppCompatActivity) : GitOperation(fileDir, callingActivity) {
+class SyncOperation(callingActivity: AppCompatActivity) : GitOperation(callingActivity) {
override val commands = arrayOf(
// Stage all files
@@ -28,8 +20,4 @@ class SyncOperation(fileDir: File, callingActivity: AppCompatActivity) : GitOper
// Push it all back
git.push().setPushAll().setRemote("origin"),
)
-
- override suspend fun execute() {
- GitCommandExecutor(callingActivity, this).execute()
- }
}