Add setting to save OpenKeychain auth keyid (#554)
* Add setting to save OpenKeychain auth keyid * Hide pref not disable Co-Authored-By: Reagan Sanders <vexofp@gmail.com> Signed-off-by: Harsh Shandilya <msfjarvis@gmail.com>
This commit is contained in:
parent
2fcec8685b
commit
cdf45bc323
11 changed files with 67 additions and 15 deletions
|
@ -66,6 +66,7 @@ class UserPreference : AppCompatActivity() {
|
||||||
|
|
||||||
// Git preferences
|
// Git preferences
|
||||||
val gitServerPreference = findPreference<Preference>("git_server_info")
|
val gitServerPreference = findPreference<Preference>("git_server_info")
|
||||||
|
val openkeystoreIdPreference = findPreference<Preference>("ssh_openkeystore_clear_keyid")
|
||||||
val gitConfigPreference = findPreference<Preference>("git_config")
|
val gitConfigPreference = findPreference<Preference>("git_config")
|
||||||
val sshKeyPreference = findPreference<Preference>("ssh_key")
|
val sshKeyPreference = findPreference<Preference>("ssh_key")
|
||||||
val sshKeygenPreference = findPreference<Preference>("ssh_keygen")
|
val sshKeygenPreference = findPreference<Preference>("ssh_keygen")
|
||||||
|
@ -114,6 +115,7 @@ class UserPreference : AppCompatActivity() {
|
||||||
OpenPgpUtils.convertKeyIdToHex(java.lang.Long.valueOf(s))
|
OpenPgpUtils.convertKeyIdToHex(java.lang.Long.valueOf(s))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
openkeystoreIdPreference?.isVisible = sharedPreferences.getString("ssh_openkeystore_keyid", null)?.isNotEmpty() ?: false
|
||||||
|
|
||||||
// see if the autofill service is enabled and check the preference accordingly
|
// see if the autofill service is enabled and check the preference accordingly
|
||||||
autoFillEnablePreference?.isChecked = callingActivity.isServiceEnabled
|
autoFillEnablePreference?.isChecked = callingActivity.isServiceEnabled
|
||||||
|
@ -156,6 +158,12 @@ class UserPreference : AppCompatActivity() {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openkeystoreIdPreference?.onPreferenceClickListener = ClickListener {
|
||||||
|
sharedPreferences.edit().putString("ssh_openkeystore_keyid", null).apply()
|
||||||
|
it.isVisible = false
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
gitServerPreference?.onPreferenceClickListener = ClickListener {
|
gitServerPreference?.onPreferenceClickListener = ClickListener {
|
||||||
val intent = Intent(callingActivity, GitActivity::class.java)
|
val intent = Intent(callingActivity, GitActivity::class.java)
|
||||||
intent.putExtra("Operation", GitActivity.EDIT_SERVER)
|
intent.putExtra("Operation", GitActivity.EDIT_SERVER)
|
||||||
|
|
|
@ -64,6 +64,7 @@ class CloneOperation(fileDir: File, callingActivity: Activity) : GitOperation(fi
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onError(errorMessage: String) {
|
override fun onError(errorMessage: String) {
|
||||||
|
super.onError(errorMessage)
|
||||||
MaterialAlertDialogBuilder(callingActivity)
|
MaterialAlertDialogBuilder(callingActivity)
|
||||||
.setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title))
|
.setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title))
|
||||||
.setMessage("Error occured during the clone operation, " +
|
.setMessage("Error occured during the clone operation, " +
|
||||||
|
|
|
@ -613,7 +613,7 @@ open class GitActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onActivityResult(
|
public override fun onActivityResult(
|
||||||
requestCode: Int,
|
requestCode: Int,
|
||||||
resultCode: Int,
|
resultCode: Int,
|
||||||
data: Intent?
|
data: Intent?
|
||||||
|
@ -625,9 +625,17 @@ open class GitActivity : AppCompatActivity() {
|
||||||
// background thread - the actual signing of the SSH challenge. We pass through the
|
// background thread - the actual signing of the SSH challenge. We pass through the
|
||||||
// completed signature to the ApiIdentity, which will be blocked in the other thread
|
// completed signature to the ApiIdentity, which will be blocked in the other thread
|
||||||
// waiting for it.
|
// waiting for it.
|
||||||
if (requestCode == SshApiSessionFactory.POST_SIGNATURE && identity != null)
|
if (requestCode == SshApiSessionFactory.POST_SIGNATURE && identity != null) {
|
||||||
identity!!.postSignature(data)
|
identity!!.postSignature(data)
|
||||||
|
|
||||||
|
// If the signature failed (usually because it was cancelled), reset state
|
||||||
|
if (data == null) {
|
||||||
|
identity = null
|
||||||
|
identityBuilder = null
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (resultCode == AppCompatActivity.RESULT_CANCELED) {
|
if (resultCode == AppCompatActivity.RESULT_CANCELED) {
|
||||||
setResult(AppCompatActivity.RESULT_CANCELED)
|
setResult(AppCompatActivity.RESULT_CANCELED)
|
||||||
finish()
|
finish()
|
||||||
|
|
|
@ -235,14 +235,11 @@ abstract class GitOperation(fileDir: File, internal val callingActivity: Activit
|
||||||
* Action to execute on error
|
* Action to execute on error
|
||||||
*/
|
*/
|
||||||
open fun onError(errorMessage: String) {
|
open fun onError(errorMessage: String) {
|
||||||
MaterialAlertDialogBuilder(callingActivity)
|
if (SshSessionFactory.getInstance() is SshApiSessionFactory) {
|
||||||
.setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title))
|
// Clear stored key id from settings on auth failure
|
||||||
.setMessage(callingActivity.resources.getString(R.string.jgit_error_dialog_text) + errorMessage)
|
PreferenceManager.getDefaultSharedPreferences(callingActivity.applicationContext)
|
||||||
.setPositiveButton(callingActivity.resources.getString(R.string.dialog_ok)) { _, _ ->
|
.edit().putString("ssh_openkeystore_keyid", null).apply()
|
||||||
callingActivity.setResult(Activity.RESULT_CANCELED)
|
|
||||||
callingActivity.finish()
|
|
||||||
}
|
}
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -38,6 +38,7 @@ class PullOperation(fileDir: File, callingActivity: Activity) : GitOperation(fil
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onError(errorMessage: String) {
|
override fun onError(errorMessage: String) {
|
||||||
|
super.onError(errorMessage)
|
||||||
MaterialAlertDialogBuilder(callingActivity)
|
MaterialAlertDialogBuilder(callingActivity)
|
||||||
.setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title))
|
.setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title))
|
||||||
.setMessage("Error occured during the pull operation, " +
|
.setMessage("Error occured during the pull operation, " +
|
||||||
|
|
|
@ -39,6 +39,7 @@ class PushOperation(fileDir: File, callingActivity: Activity) : GitOperation(fil
|
||||||
|
|
||||||
override fun onError(errorMessage: String) {
|
override fun onError(errorMessage: String) {
|
||||||
// TODO handle the "Nothing to push" case
|
// TODO handle the "Nothing to push" case
|
||||||
|
super.onError(errorMessage)
|
||||||
MaterialAlertDialogBuilder(callingActivity)
|
MaterialAlertDialogBuilder(callingActivity)
|
||||||
.setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title))
|
.setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title))
|
||||||
.setMessage(callingActivity.getString(R.string.jgit_error_push_dialog_text) + errorMessage)
|
.setMessage(callingActivity.getString(R.string.jgit_error_push_dialog_text) + errorMessage)
|
||||||
|
|
|
@ -44,6 +44,7 @@ class ResetToRemoteOperation(fileDir: File, callingActivity: Activity) : GitOper
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onError(errorMessage: String) {
|
override fun onError(errorMessage: String) {
|
||||||
|
super.onError(errorMessage)
|
||||||
MaterialAlertDialogBuilder(callingActivity)
|
MaterialAlertDialogBuilder(callingActivity)
|
||||||
.setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title))
|
.setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title))
|
||||||
.setMessage("Error occured during the sync operation, " +
|
.setMessage("Error occured during the sync operation, " +
|
||||||
|
|
|
@ -52,6 +52,7 @@ class SyncOperation(fileDir: File, callingActivity: Activity) : GitOperation(fil
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onError(errorMessage: String) {
|
override fun onError(errorMessage: String) {
|
||||||
|
super.onError(errorMessage)
|
||||||
MaterialAlertDialogBuilder(callingActivity)
|
MaterialAlertDialogBuilder(callingActivity)
|
||||||
.setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title))
|
.setTitle(callingActivity.resources.getString(R.string.jgit_error_dialog_title))
|
||||||
.setMessage("Error occured during the sync operation, " +
|
.setMessage("Error occured during the sync operation, " +
|
||||||
|
|
|
@ -8,7 +8,9 @@ import android.app.Activity;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentSender;
|
import android.content.IntentSender;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.preference.PreferenceManager;
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||||
import com.jcraft.jsch.Identity;
|
import com.jcraft.jsch.Identity;
|
||||||
import com.jcraft.jsch.JSch;
|
import com.jcraft.jsch.JSch;
|
||||||
|
@ -16,6 +18,7 @@ import com.jcraft.jsch.JSchException;
|
||||||
import com.jcraft.jsch.Session;
|
import com.jcraft.jsch.Session;
|
||||||
import com.jcraft.jsch.UserInfo;
|
import com.jcraft.jsch.UserInfo;
|
||||||
import com.zeapo.pwdstore.R;
|
import com.zeapo.pwdstore.R;
|
||||||
|
import com.zeapo.pwdstore.git.GitActivity;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import org.eclipse.jgit.errors.UnsupportedCredentialItem;
|
import org.eclipse.jgit.errors.UnsupportedCredentialItem;
|
||||||
|
@ -104,7 +107,8 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
|
||||||
private SshAuthenticationApi api;
|
private SshAuthenticationApi api;
|
||||||
private String keyId, description, alg;
|
private String keyId, description, alg;
|
||||||
private byte[] publicKey;
|
private byte[] publicKey;
|
||||||
private Activity callingActivity;
|
private GitActivity callingActivity;
|
||||||
|
private SharedPreferences settings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new IdentityBuilder
|
* Construct a new IdentityBuilder
|
||||||
|
@ -112,7 +116,7 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
|
||||||
* @param callingActivity Activity that will be used to launch pending intents and that will
|
* @param callingActivity Activity that will be used to launch pending intents and that will
|
||||||
* receive and handle the results.
|
* receive and handle the results.
|
||||||
*/
|
*/
|
||||||
public IdentityBuilder(Activity callingActivity) {
|
public IdentityBuilder(GitActivity callingActivity) {
|
||||||
this.callingActivity = callingActivity;
|
this.callingActivity = callingActivity;
|
||||||
|
|
||||||
List<String> providers =
|
List<String> providers =
|
||||||
|
@ -124,6 +128,11 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
|
||||||
// TODO: Handle multiple available providers? Are there actually any in practice beyond
|
// TODO: Handle multiple available providers? Are there actually any in practice beyond
|
||||||
// OpenKeychain?
|
// OpenKeychain?
|
||||||
connection = new SshAuthenticationConnection(callingActivity, providers.get(0));
|
connection = new SshAuthenticationConnection(callingActivity, providers.get(0));
|
||||||
|
|
||||||
|
settings =
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(
|
||||||
|
callingActivity.getApplicationContext());
|
||||||
|
keyId = settings.getString("ssh_openkeystore_keyid", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Free any resources associated with this IdentityBuilder */
|
/** Free any resources associated with this IdentityBuilder */
|
||||||
|
@ -146,7 +155,24 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
|
||||||
case SshAuthenticationApi.RESULT_CODE_ERROR:
|
case SshAuthenticationApi.RESULT_CODE_ERROR:
|
||||||
SshAuthenticationApiError error =
|
SshAuthenticationApiError error =
|
||||||
result.getParcelableExtra(SshAuthenticationApi.EXTRA_ERROR);
|
result.getParcelableExtra(SshAuthenticationApi.EXTRA_ERROR);
|
||||||
|
// On an OpenKeychain SSH API error, clear out the stored keyid
|
||||||
|
settings.edit().putString("ssh_openkeystore_keyid", null).apply();
|
||||||
|
|
||||||
|
switch (error.getError()) {
|
||||||
|
// If the problem was just a bad keyid, reset to allow them to choose a
|
||||||
|
// different one
|
||||||
|
case (SshAuthenticationApiError.NO_SUCH_KEY):
|
||||||
|
case (SshAuthenticationApiError.NO_AUTH_KEY):
|
||||||
|
keyId = null;
|
||||||
|
publicKey = null;
|
||||||
|
description = null;
|
||||||
|
alg = null;
|
||||||
|
return executeApi(new KeySelectionRequest(), requestCode);
|
||||||
|
|
||||||
|
// Other errors are fatal
|
||||||
|
default:
|
||||||
throw new RuntimeException(error.getMessage());
|
throw new RuntimeException(error.getMessage());
|
||||||
|
}
|
||||||
case SshAuthenticationApi.RESULT_CODE_SUCCESS:
|
case SshAuthenticationApi.RESULT_CODE_SUCCESS:
|
||||||
break;
|
break;
|
||||||
case SshAuthenticationApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
|
case SshAuthenticationApi.RESULT_CODE_USER_INTERACTION_REQUIRED:
|
||||||
|
@ -181,6 +207,7 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
|
||||||
if (intent.hasExtra(SshAuthenticationApi.EXTRA_KEY_ID)) {
|
if (intent.hasExtra(SshAuthenticationApi.EXTRA_KEY_ID)) {
|
||||||
keyId = intent.getStringExtra(SshAuthenticationApi.EXTRA_KEY_ID);
|
keyId = intent.getStringExtra(SshAuthenticationApi.EXTRA_KEY_ID);
|
||||||
description = intent.getStringExtra(SshAuthenticationApi.EXTRA_KEY_DESCRIPTION);
|
description = intent.getStringExtra(SshAuthenticationApi.EXTRA_KEY_DESCRIPTION);
|
||||||
|
settings.edit().putString("ssh_openkeystore_keyid", keyId).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (intent.hasExtra(SshAuthenticationApi.EXTRA_SSH_PUBLIC_KEY)) {
|
if (intent.hasExtra(SshAuthenticationApi.EXTRA_SSH_PUBLIC_KEY)) {
|
||||||
|
@ -209,7 +236,8 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
|
||||||
// We can immediately try the next phase without needing to post
|
// We can immediately try the next phase without needing to post
|
||||||
// back
|
// back
|
||||||
// though onActivityResult
|
// though onActivityResult
|
||||||
tryBuild(requestCode);
|
callingActivity.onActivityResult(
|
||||||
|
requestCode, Activity.RESULT_OK, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -342,7 +370,9 @@ public class SshApiSessionFactory extends GitConfigSessionFactory {
|
||||||
*/
|
*/
|
||||||
public void postSignature(Intent data) {
|
public void postSignature(Intent data) {
|
||||||
try {
|
try {
|
||||||
|
if (data != null) {
|
||||||
signature = handleSignResult(data);
|
signature = handleSignResult(data);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (latch != null) latch.countDown();
|
if (latch != null) latch.countDown();
|
||||||
}
|
}
|
||||||
|
|
|
@ -271,4 +271,5 @@
|
||||||
<string name="biometric_auth_title">Enable biometric authentication</string>
|
<string name="biometric_auth_title">Enable biometric authentication</string>
|
||||||
<string name="biometric_auth_summary">When enabled, Password Store will prompt you for your fingerprint when launching the app</string>
|
<string name="biometric_auth_summary">When enabled, Password Store will prompt you for your fingerprint when launching the app</string>
|
||||||
<string name="biometric_auth_summary_error">Fingerprint hardware not accessible or missing</string>
|
<string name="biometric_auth_summary_error">Fingerprint hardware not accessible or missing</string>
|
||||||
|
<string name="ssh_openkeystore_clear_keyid">Clear remembered OpenKeystore SSH Key ID</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -19,6 +19,9 @@
|
||||||
<androidx.preference.Preference
|
<androidx.preference.Preference
|
||||||
android:key="hotp_remember_clear_choice"
|
android:key="hotp_remember_clear_choice"
|
||||||
android:title="@string/hotp_remember_clear_choice" />
|
android:title="@string/hotp_remember_clear_choice" />
|
||||||
|
<androidx.preference.Preference
|
||||||
|
android:key="ssh_openkeystore_clear_keyid"
|
||||||
|
android:title="@string/ssh_openkeystore_clear_keyid" />
|
||||||
<androidx.preference.Preference
|
<androidx.preference.Preference
|
||||||
android:key="ssh_see_key"
|
android:key="ssh_see_key"
|
||||||
android:title="@string/pref_ssh_see_key_title" />
|
android:title="@string/pref_ssh_see_key_title" />
|
||||||
|
|
Loading…
Reference in a new issue