mirror of
https://github.com/android-password-store/Android-Password-Store
synced 2025-08-30 22:05:19 +00:00
Better guidance for users to deal with host key changes (#1242)
* Provide actionable guidance for host key mismatches Signed-off-by: Harsh Shandilya <me@msfjarvis.dev> * Update changelog Signed-off-by: Harsh Shandilya <me@msfjarvis.dev> * Hide host key clear button after use Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
@@ -17,6 +17,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
- Decrypt screen would stay in memory infinitely, allowing passwords to be seen without re-auth
|
- Decrypt screen would stay in memory infinitely, allowing passwords to be seen without re-auth
|
||||||
- Git commits in the store would wrongly use the 'default' committer as opposed to the user's configured one
|
- Git commits in the store would wrongly use the 'default' committer as opposed to the user's configured one
|
||||||
- Connection attempts now use a reasonable 10 second timeout as opposed to the default of 30 seconds
|
- Connection attempts now use a reasonable 10 second timeout as opposed to the default of 30 seconds
|
||||||
|
- A change to the remote host key for a server would prevent the user from being able to connect to it
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
@@ -29,6 +29,7 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import net.schmizz.sshj.common.DisconnectReason
|
import net.schmizz.sshj.common.DisconnectReason
|
||||||
import net.schmizz.sshj.common.SSHException
|
import net.schmizz.sshj.common.SSHException
|
||||||
|
import net.schmizz.sshj.transport.TransportException
|
||||||
import net.schmizz.sshj.userauth.UserAuthException
|
import net.schmizz.sshj.userauth.UserAuthException
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,6 +77,10 @@ abstract class BaseGitActivity : ContinuationContainerActivity() {
|
|||||||
if (err.message?.contains("cannot open additional channels") == true) {
|
if (err.message?.contains("cannot open additional channels") == true) {
|
||||||
GitSettings.useMultiplexing = false
|
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.")
|
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 if (err is TransportException && err.disconnectReason == DisconnectReason.HOST_KEY_NOT_VERIFIABLE) {
|
||||||
|
SSHException(DisconnectReason.HOST_KEY_NOT_VERIFIABLE,
|
||||||
|
"WARNING: The remote host key has changed. If this is expected, please go to Git server settings and clear the saved host key."
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
err
|
err
|
||||||
}
|
}
|
||||||
|
@@ -84,6 +84,12 @@ class GitServerConfigActivity : BaseGitActivity() {
|
|||||||
setAuthModes(text.startsWith("http://") || text.startsWith("https://"))
|
setAuthModes(text.startsWith("http://") || text.startsWith("https://"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binding.clearHostKeyButton.isVisible = GitSettings.hasSavedHostKey()
|
||||||
|
binding.clearHostKeyButton.setOnClickListener {
|
||||||
|
GitSettings.clearSavedHostKey()
|
||||||
|
Snackbar.make(binding.root, getString(R.string.clear_saved_host_key_success), Snackbar.LENGTH_LONG).show()
|
||||||
|
it.isVisible = false
|
||||||
|
}
|
||||||
binding.saveButton.setOnClickListener {
|
binding.saveButton.setOnClickListener {
|
||||||
val newUrl = binding.serverUrl.text.toString().trim()
|
val newUrl = binding.serverUrl.text.toString().trim()
|
||||||
// If url is of type john_doe@example.org:12435/path/to/repo, then not adding `ssh://`
|
// If url is of type john_doe@example.org:12435/path/to/repo, then not adding `ssh://`
|
||||||
|
@@ -55,6 +55,7 @@ object GitSettings {
|
|||||||
private val settings by lazy(LazyThreadSafetyMode.PUBLICATION) { Application.instance.sharedPrefs }
|
private val settings by lazy(LazyThreadSafetyMode.PUBLICATION) { Application.instance.sharedPrefs }
|
||||||
private val encryptedSettings by lazy(LazyThreadSafetyMode.PUBLICATION) { Application.instance.getEncryptedGitPrefs() }
|
private val encryptedSettings by lazy(LazyThreadSafetyMode.PUBLICATION) { Application.instance.getEncryptedGitPrefs() }
|
||||||
private val proxySettings by lazy(LazyThreadSafetyMode.PUBLICATION) { Application.instance.getEncryptedProxyPrefs() }
|
private val proxySettings by lazy(LazyThreadSafetyMode.PUBLICATION) { Application.instance.getEncryptedProxyPrefs() }
|
||||||
|
private val hostKeyPath by lazy(LazyThreadSafetyMode.NONE) { "${Application.instance.filesDir}/.host_key" }
|
||||||
|
|
||||||
var authMode
|
var authMode
|
||||||
get() = AuthMode.fromString(settings.getString(PreferenceKeys.GIT_REMOTE_AUTH))
|
get() = AuthMode.fromString(settings.getString(PreferenceKeys.GIT_REMOTE_AUTH))
|
||||||
@@ -63,6 +64,7 @@ object GitSettings {
|
|||||||
putString(PreferenceKeys.GIT_REMOTE_AUTH, value.pref)
|
putString(PreferenceKeys.GIT_REMOTE_AUTH, value.pref)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var url
|
var url
|
||||||
get() = settings.getString(PreferenceKeys.GIT_REMOTE_URL)
|
get() = settings.getString(PreferenceKeys.GIT_REMOTE_URL)
|
||||||
private set(value) {
|
private set(value) {
|
||||||
@@ -78,8 +80,9 @@ object GitSettings {
|
|||||||
// should be deleted/reset.
|
// should be deleted/reset.
|
||||||
useMultiplexing = true
|
useMultiplexing = true
|
||||||
encryptedSettings.edit { remove(PreferenceKeys.HTTPS_PASSWORD) }
|
encryptedSettings.edit { remove(PreferenceKeys.HTTPS_PASSWORD) }
|
||||||
File("${Application.instance.filesDir}/.host_key").delete()
|
clearSavedHostKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
var authorName
|
var authorName
|
||||||
get() = settings.getString(PreferenceKeys.GIT_CONFIG_AUTHOR_NAME) ?: ""
|
get() = settings.getString(PreferenceKeys.GIT_CONFIG_AUTHOR_NAME) ?: ""
|
||||||
set(value) {
|
set(value) {
|
||||||
@@ -87,6 +90,7 @@ object GitSettings {
|
|||||||
putString(PreferenceKeys.GIT_CONFIG_AUTHOR_NAME, value)
|
putString(PreferenceKeys.GIT_CONFIG_AUTHOR_NAME, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var authorEmail
|
var authorEmail
|
||||||
get() = settings.getString(PreferenceKeys.GIT_CONFIG_AUTHOR_EMAIL) ?: ""
|
get() = settings.getString(PreferenceKeys.GIT_CONFIG_AUTHOR_EMAIL) ?: ""
|
||||||
set(value) {
|
set(value) {
|
||||||
@@ -94,6 +98,7 @@ object GitSettings {
|
|||||||
putString(PreferenceKeys.GIT_CONFIG_AUTHOR_EMAIL, value)
|
putString(PreferenceKeys.GIT_CONFIG_AUTHOR_EMAIL, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var branch
|
var branch
|
||||||
get() = settings.getString(PreferenceKeys.GIT_BRANCH_NAME) ?: DEFAULT_BRANCH
|
get() = settings.getString(PreferenceKeys.GIT_BRANCH_NAME) ?: DEFAULT_BRANCH
|
||||||
private set(value) {
|
private set(value) {
|
||||||
@@ -101,6 +106,7 @@ object GitSettings {
|
|||||||
putString(PreferenceKeys.GIT_BRANCH_NAME, value)
|
putString(PreferenceKeys.GIT_BRANCH_NAME, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var useMultiplexing
|
var useMultiplexing
|
||||||
get() = settings.getBoolean(PreferenceKeys.GIT_REMOTE_USE_MULTIPLEXING, true)
|
get() = settings.getBoolean(PreferenceKeys.GIT_REMOTE_USE_MULTIPLEXING, true)
|
||||||
set(value) {
|
set(value) {
|
||||||
@@ -178,4 +184,16 @@ object GitSettings {
|
|||||||
branch = newBranch
|
branch = newBranch
|
||||||
return UpdateConnectionSettingsResult.Valid
|
return UpdateConnectionSettingsResult.Valid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a previously saved SSH host key
|
||||||
|
*/
|
||||||
|
fun clearSavedHostKey() {
|
||||||
|
File(hostKeyPath).delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if a host key was previously saved
|
||||||
|
*/
|
||||||
|
fun hasSavedHostKey(): Boolean = File(hostKeyPath).exists()
|
||||||
}
|
}
|
||||||
|
@@ -11,7 +11,7 @@
|
|||||||
android:background="?android:attr/windowBackground"
|
android:background="?android:attr/windowBackground"
|
||||||
android:padding="@dimen/activity_horizontal_margin"
|
android:padding="@dimen/activity_horizontal_margin"
|
||||||
tools:background="@color/white"
|
tools:background="@color/white"
|
||||||
tools:context="dev.msfjarvis.aps.git.GitServerConfigActivity">
|
tools:context="dev.msfjarvis.aps.ui.git.config.GitServerConfigActivity">
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -120,5 +120,16 @@
|
|||||||
android:text="@string/crypto_save"
|
android:text="@string/crypto_save"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/auth_mode_group" />
|
app:layout_constraintTop_toBottomOf="@id/auth_mode_group" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/clear_host_key_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="@string/clear_saved_host_key"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/auth_mode_group"
|
||||||
|
tools:visibility="visible" />
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
@@ -398,5 +398,7 @@
|
|||||||
<string name="pref_proxy_settings">HTTP(S) proxy settings</string>
|
<string name="pref_proxy_settings">HTTP(S) proxy settings</string>
|
||||||
<string name="invalid_proxy_url">Invalid URL</string>
|
<string name="invalid_proxy_url">Invalid URL</string>
|
||||||
<string name="oreo_autofill_password_fill_and_conditional_save_support">Fill and save passwords (saving requires that no accessibility services are enabled)</string>
|
<string name="oreo_autofill_password_fill_and_conditional_save_support">Fill and save passwords (saving requires that no accessibility services are enabled)</string>
|
||||||
|
<string name="clear_saved_host_key">Clear saved host key</string>
|
||||||
|
<string name="clear_saved_host_key_success">Successfully cleared saved host key!</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
Reference in New Issue
Block a user