Add migration to new SshKey backend (#1076)
Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
parent
e731943437
commit
4214b7fbb4
7 changed files with 35 additions and 12 deletions
|
@ -12,14 +12,17 @@ import com.github.ajalt.timberkt.e
|
||||||
import com.github.ajalt.timberkt.i
|
import com.github.ajalt.timberkt.i
|
||||||
import com.zeapo.pwdstore.git.config.GitSettings
|
import com.zeapo.pwdstore.git.config.GitSettings
|
||||||
import com.zeapo.pwdstore.git.config.Protocol
|
import com.zeapo.pwdstore.git.config.Protocol
|
||||||
|
import com.zeapo.pwdstore.git.sshj.SshKey
|
||||||
import com.zeapo.pwdstore.utils.PreferenceKeys
|
import com.zeapo.pwdstore.utils.PreferenceKeys
|
||||||
import com.zeapo.pwdstore.utils.getString
|
import com.zeapo.pwdstore.utils.getString
|
||||||
import com.zeapo.pwdstore.utils.sharedPrefs
|
import com.zeapo.pwdstore.utils.sharedPrefs
|
||||||
|
import java.io.File
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
|
||||||
fun runMigrations(context: Context) {
|
fun runMigrations(context: Context) {
|
||||||
migrateToGitUrlBasedConfig(context)
|
migrateToGitUrlBasedConfig(context)
|
||||||
migrateToHideAll(context)
|
migrateToHideAll(context)
|
||||||
|
migrateToSshKey(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun migrateToGitUrlBasedConfig(context: Context) {
|
private fun migrateToGitUrlBasedConfig(context: Context) {
|
||||||
|
@ -94,3 +97,19 @@ private fun migrateToHideAll(context: Context) {
|
||||||
putBoolean(PreferenceKeys.SHOW_HIDDEN_CONTENTS, isHidden)
|
putBoolean(PreferenceKeys.SHOW_HIDDEN_CONTENTS, isHidden)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun migrateToSshKey(context: Context) {
|
||||||
|
val privateKeyFile = File(context.filesDir, ".ssh_key")
|
||||||
|
if (context.sharedPrefs.contains(PreferenceKeys.USE_GENERATED_KEY) &&
|
||||||
|
!SshKey.exists &&
|
||||||
|
privateKeyFile.exists()) {
|
||||||
|
// Currently uses a private key imported or generated with an old version of Password Store.
|
||||||
|
// Generated keys come with a public key which the user should still be able to view after
|
||||||
|
// the migration (not possible for regular imported keys), hence the special case.
|
||||||
|
val isGeneratedKey = context.sharedPrefs.getBoolean(PreferenceKeys.USE_GENERATED_KEY, false)
|
||||||
|
SshKey.useLegacyKey(isGeneratedKey)
|
||||||
|
context.sharedPrefs.edit {
|
||||||
|
remove(PreferenceKeys.USE_GENERATED_KEY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -46,7 +46,6 @@ import com.zeapo.pwdstore.crypto.BasePgpActivity.Companion.getLongName
|
||||||
import com.zeapo.pwdstore.crypto.DecryptActivity
|
import com.zeapo.pwdstore.crypto.DecryptActivity
|
||||||
import com.zeapo.pwdstore.crypto.PasswordCreationActivity
|
import com.zeapo.pwdstore.crypto.PasswordCreationActivity
|
||||||
import com.zeapo.pwdstore.git.BaseGitActivity
|
import com.zeapo.pwdstore.git.BaseGitActivity
|
||||||
import com.zeapo.pwdstore.git.GitServerConfigActivity
|
|
||||||
import com.zeapo.pwdstore.git.config.AuthMode
|
import com.zeapo.pwdstore.git.config.AuthMode
|
||||||
import com.zeapo.pwdstore.git.config.GitSettings
|
import com.zeapo.pwdstore.git.config.GitSettings
|
||||||
import com.zeapo.pwdstore.ui.dialogs.FolderCreationDialogFragment
|
import com.zeapo.pwdstore.ui.dialogs.FolderCreationDialogFragment
|
||||||
|
@ -89,12 +88,6 @@ class PasswordStore : BaseGitActivity() {
|
||||||
ViewModelProvider.AndroidViewModelFactory(application)
|
ViewModelProvider.AndroidViewModelFactory(application)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val listRefreshAction = registerForActivityResult(StartActivityForResult()) { result ->
|
|
||||||
if (result.resultCode == RESULT_OK) {
|
|
||||||
refreshPasswordList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
||||||
// open search view on search key, or Ctr+F
|
// open search view on search key, or Ctr+F
|
||||||
if ((keyCode == KeyEvent.KEYCODE_SEARCH || keyCode == KeyEvent.KEYCODE_F && event.isCtrlPressed) &&
|
if ((keyCode == KeyEvent.KEYCODE_SEARCH || keyCode == KeyEvent.KEYCODE_F && event.isCtrlPressed) &&
|
||||||
|
|
|
@ -10,8 +10,8 @@ import com.github.ajalt.timberkt.Timber.tag
|
||||||
import com.github.ajalt.timberkt.d
|
import com.github.ajalt.timberkt.d
|
||||||
import com.github.ajalt.timberkt.e
|
import com.github.ajalt.timberkt.e
|
||||||
import com.github.michaelbull.result.Err
|
import com.github.michaelbull.result.Err
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import com.github.michaelbull.result.Result
|
import com.github.michaelbull.result.Result
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.zeapo.pwdstore.R
|
import com.zeapo.pwdstore.R
|
||||||
import com.zeapo.pwdstore.git.config.GitSettings
|
import com.zeapo.pwdstore.git.config.GitSettings
|
||||||
import com.zeapo.pwdstore.git.operation.BreakOutOfDetached
|
import com.zeapo.pwdstore.git.operation.BreakOutOfDetached
|
||||||
|
|
|
@ -7,6 +7,7 @@ package com.zeapo.pwdstore.git
|
||||||
|
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
import com.github.michaelbull.result.Result
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.zeapo.pwdstore.R
|
import com.zeapo.pwdstore.R
|
||||||
import com.zeapo.pwdstore.git.GitException.PullException
|
import com.zeapo.pwdstore.git.GitException.PullException
|
||||||
|
@ -14,7 +15,6 @@ import com.zeapo.pwdstore.git.GitException.PushException
|
||||||
import com.zeapo.pwdstore.git.config.GitSettings
|
import com.zeapo.pwdstore.git.config.GitSettings
|
||||||
import com.zeapo.pwdstore.git.operation.GitOperation
|
import com.zeapo.pwdstore.git.operation.GitOperation
|
||||||
import com.zeapo.pwdstore.utils.snackbar
|
import com.zeapo.pwdstore.utils.snackbar
|
||||||
import com.github.michaelbull.result.Result
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.eclipse.jgit.api.CommitCommand
|
import org.eclipse.jgit.api.CommitCommand
|
||||||
|
|
|
@ -76,7 +76,7 @@ object SshKey {
|
||||||
val sshPublicKey
|
val sshPublicKey
|
||||||
get() = if (publicKeyFile.exists()) publicKeyFile.readText() else null
|
get() = if (publicKeyFile.exists()) publicKeyFile.readText() else null
|
||||||
val canShowSshPublicKey
|
val canShowSshPublicKey
|
||||||
get() = type in listOf(Type.KeystoreNative, Type.KeystoreWrappedEd25519)
|
get() = type in listOf(Type.LegacyGenerated, Type.KeystoreNative, Type.KeystoreWrappedEd25519)
|
||||||
val exists
|
val exists
|
||||||
get() = type != null
|
get() = type != null
|
||||||
val mustAuthenticate: Boolean
|
val mustAuthenticate: Boolean
|
||||||
|
@ -128,6 +128,9 @@ object SshKey {
|
||||||
Imported("imported"),
|
Imported("imported"),
|
||||||
KeystoreNative("keystore_native"),
|
KeystoreNative("keystore_native"),
|
||||||
KeystoreWrappedEd25519("keystore_wrapped_ed25519"),
|
KeystoreWrappedEd25519("keystore_wrapped_ed25519"),
|
||||||
|
|
||||||
|
// Behaves like `Imported`, but allows to view the public key.
|
||||||
|
LegacyGenerated("legacy_generated"),
|
||||||
;
|
;
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -204,6 +207,11 @@ object SshKey {
|
||||||
type = Type.Imported
|
type = Type.Imported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated("To be used only in Migrations.kt")
|
||||||
|
fun useLegacyKey(isGenerated: Boolean) {
|
||||||
|
type = if (isGenerated) Type.LegacyGenerated else Type.Imported
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
@Suppress("BlockingMethodInNonBlockingContext")
|
||||||
private suspend fun getOrCreateWrappingMasterKey(requireAuthentication: Boolean) = withContext(Dispatchers.IO) {
|
private suspend fun getOrCreateWrappingMasterKey(requireAuthentication: Boolean) = withContext(Dispatchers.IO) {
|
||||||
MasterKey.Builder(context, KEYSTORE_ALIAS).run {
|
MasterKey.Builder(context, KEYSTORE_ALIAS).run {
|
||||||
|
@ -268,7 +276,7 @@ object SshKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun provide(client: SSHClient, passphraseFinder: InteractivePasswordFinder): KeyProvider? = when (type) {
|
fun provide(client: SSHClient, passphraseFinder: InteractivePasswordFinder): KeyProvider? = when (type) {
|
||||||
Type.Imported -> client.loadKeys(privateKeyFile.absolutePath, passphraseFinder)
|
Type.LegacyGenerated, Type.Imported -> client.loadKeys(privateKeyFile.absolutePath, passphraseFinder)
|
||||||
Type.KeystoreNative -> KeystoreNativeKeyProvider
|
Type.KeystoreNative -> KeystoreNativeKeyProvider
|
||||||
Type.KeystoreWrappedEd25519 -> KeystoreWrappedEd25519KeyProvider
|
Type.KeystoreWrappedEd25519 -> KeystoreWrappedEd25519KeyProvider
|
||||||
null -> null
|
null -> null
|
||||||
|
|
|
@ -178,7 +178,7 @@ open class PasswordRepository protected constructor() {
|
||||||
val dir = getRepositoryDirectory()
|
val dir = getRepositoryDirectory()
|
||||||
// uninitialize the repo if the dir does not exist or is absolutely empty
|
// uninitialize the repo if the dir does not exist or is absolutely empty
|
||||||
settings.edit {
|
settings.edit {
|
||||||
if (!dir.exists() || !dir.isDirectory || dir.listFiles()?.isEmpty() == true) {
|
if (!dir.exists() || !dir.isDirectory || requireNotNull(dir.listFiles()).isEmpty()) {
|
||||||
putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, false)
|
putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, false)
|
||||||
} else {
|
} else {
|
||||||
putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, true)
|
putBoolean(PreferenceKeys.REPOSITORY_INITIALIZED, true)
|
||||||
|
|
|
@ -76,4 +76,7 @@ object PreferenceKeys {
|
||||||
const val SSH_OPENKEYSTORE_CLEAR_KEY_ID = "ssh_openkeystore_clear_keyid"
|
const val SSH_OPENKEYSTORE_CLEAR_KEY_ID = "ssh_openkeystore_clear_keyid"
|
||||||
const val SSH_OPENKEYSTORE_KEYID = "ssh_openkeystore_keyid"
|
const val SSH_OPENKEYSTORE_KEYID = "ssh_openkeystore_keyid"
|
||||||
const val SSH_SEE_KEY = "ssh_see_key"
|
const val SSH_SEE_KEY = "ssh_see_key"
|
||||||
|
|
||||||
|
@Deprecated("To be used only in Migrations.kt")
|
||||||
|
const val USE_GENERATED_KEY = "use_generated_key"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue