aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java/com/zeapo
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/com/zeapo')
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/GitAsyncTask.kt2
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/git/config/SshjSessionFactory.kt45
-rw-r--r--app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt6
3 files changed, 49 insertions, 4 deletions
diff --git a/app/src/main/java/com/zeapo/pwdstore/git/GitAsyncTask.kt b/app/src/main/java/com/zeapo/pwdstore/git/GitAsyncTask.kt
index 1fd5f9e5..188891bd 100644
--- a/app/src/main/java/com/zeapo/pwdstore/git/GitAsyncTask.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/git/GitAsyncTask.kt
@@ -12,6 +12,7 @@ import android.os.AsyncTask
import com.github.ajalt.timberkt.e
import com.zeapo.pwdstore.PasswordStore
import com.zeapo.pwdstore.R
+import com.zeapo.pwdstore.git.config.SshjSessionFactory
import net.schmizz.sshj.userauth.UserAuthException
import org.eclipse.jgit.api.CommitCommand
import org.eclipse.jgit.api.GitCommand
@@ -147,6 +148,7 @@ class GitAsyncTask(
if (refreshListOnEnd) {
(activity as? PasswordStore)?.resetPasswordList()
}
+ (SshSessionFactory.getInstance() as? SshjSessionFactory)?.clearCredentials()
SshSessionFactory.setInstance(null)
}
diff --git a/app/src/main/java/com/zeapo/pwdstore/git/config/SshjSessionFactory.kt b/app/src/main/java/com/zeapo/pwdstore/git/config/SshjSessionFactory.kt
index 146fdb43..4d961137 100644
--- a/app/src/main/java/com/zeapo/pwdstore/git/config/SshjSessionFactory.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/git/config/SshjSessionFactory.kt
@@ -7,6 +7,7 @@ package com.zeapo.pwdstore.git.config
import android.util.Base64
import com.github.ajalt.timberkt.d
import com.github.ajalt.timberkt.w
+import com.zeapo.pwdstore.utils.clear
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import net.schmizz.sshj.SSHClient
@@ -33,18 +34,48 @@ import kotlin.coroutines.Continuation
import kotlin.coroutines.suspendCoroutine
sealed class SshAuthData {
- class Password(val passwordFinder: InteractivePasswordFinder) : SshAuthData()
- class PublicKeyFile(val keyFile: File, val passphraseFinder: InteractivePasswordFinder) : SshAuthData()
+ class Password(val passwordFinder: InteractivePasswordFinder) : SshAuthData() {
+ override fun clearCredentials() {
+ passwordFinder.clearPassword()
+ }
+ }
+
+ class PublicKeyFile(val keyFile: File, val passphraseFinder: InteractivePasswordFinder) : SshAuthData() {
+ override fun clearCredentials() {
+ passphraseFinder.clearPassword()
+ }
+ }
+
+ abstract fun clearCredentials()
}
abstract class InteractivePasswordFinder : PasswordFinder {
+ abstract fun askForPassword(cont: Continuation<String?>, isRetry: Boolean)
+
private var isRetry = false
private var shouldRetry = true
+ private var lastPassword: CharArray? = null
- abstract fun askForPassword(cont: Continuation<String?>, isRetry: Boolean)
+ fun resetForReuse() {
+ isRetry = false
+ shouldRetry = true
+ }
+
+ fun clearPassword() {
+ lastPassword?.clear()
+ lastPassword = null
+ }
final override fun reqPassword(resource: Resource<*>?): CharArray {
+ if (lastPassword != null && !isRetry) {
+ // This instance successfully authenticated in a previous authentication step and is
+ // now being reused for a new one. We try the previous password so that the user
+ // does not have to type it again.
+ isRetry = true
+ return lastPassword!!
+ }
+ clearPassword()
val password = runBlocking(Dispatchers.Main) {
suspendCoroutine<String?> { cont ->
askForPassword(cont, isRetry)
@@ -52,7 +83,7 @@ abstract class InteractivePasswordFinder : PasswordFinder {
}
isRetry = true
return if (password != null) {
- password.toCharArray()
+ password.toCharArray().also { lastPassword = it }
} else {
shouldRetry = false
CharArray(0)
@@ -67,6 +98,10 @@ class SshjSessionFactory(private val username: String, private val authData: Ssh
override fun getSession(uri: URIish, credentialsProvider: CredentialsProvider?, fs: FS?, tms: Int): RemoteSession {
return SshjSession(uri, username, authData, hostKeyFile).connect()
}
+
+ fun clearCredentials() {
+ authData.clearCredentials()
+ }
}
private fun makeTofuHostKeyVerifier(hostKeyFile: File): HostKeyVerifier {
@@ -105,9 +140,11 @@ private class SshjSession(private val uri: URIish, private val username: String,
when (authData) {
is SshAuthData.Password -> {
ssh.authPassword(username, authData.passwordFinder)
+ authData.passwordFinder.resetForReuse()
}
is SshAuthData.PublicKeyFile -> {
ssh.authPublickey(username, ssh.loadKeys(authData.keyFile.absolutePath, authData.passphraseFinder))
+ authData.passphraseFinder.resetForReuse()
}
}
return this
diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt b/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt
index ed91eede..4820d8bf 100644
--- a/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt
+++ b/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt
@@ -26,6 +26,12 @@ fun String.splitLines(): Array<String> {
return split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
}
+fun CharArray.clear() {
+ forEachIndexed { i, _ ->
+ this[i] = 0.toChar()
+ }
+}
+
fun Context.resolveAttribute(attr: Int): Int {
val typedValue = TypedValue()
this.theme.resolveAttribute(attr, typedValue, true)