Refactor key import flow and implement support for replacing
This commit is contained in:
parent
b9f4da71ea
commit
4ed98c9fda
2 changed files with 67 additions and 26 deletions
|
@ -13,7 +13,10 @@ import app.passwordstore.R
|
||||||
import app.passwordstore.crypto.KeyUtils.tryGetId
|
import app.passwordstore.crypto.KeyUtils.tryGetId
|
||||||
import app.passwordstore.crypto.PGPKey
|
import app.passwordstore.crypto.PGPKey
|
||||||
import app.passwordstore.crypto.PGPKeyManager
|
import app.passwordstore.crypto.PGPKeyManager
|
||||||
import com.github.michaelbull.result.mapBoth
|
import app.passwordstore.crypto.errors.KeyAlreadyExistsException
|
||||||
|
import com.github.michaelbull.result.Err
|
||||||
|
import com.github.michaelbull.result.Ok
|
||||||
|
import com.github.michaelbull.result.Result
|
||||||
import com.github.michaelbull.result.runCatching
|
import com.github.michaelbull.result.runCatching
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
@ -23,6 +26,11 @@ import kotlinx.coroutines.runBlocking
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class PGPKeyImportActivity : AppCompatActivity() {
|
class PGPKeyImportActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [ByteArray] containing the contents of the previously selected file. This is necessary for
|
||||||
|
* the replacement case where we do not want users to have to pick the file again.
|
||||||
|
*/
|
||||||
|
private var lastBytes: ByteArray? = null
|
||||||
@Inject lateinit var keyManager: PGPKeyManager
|
@Inject lateinit var keyManager: PGPKeyManager
|
||||||
|
|
||||||
private val pgpKeyImportAction =
|
private val pgpKeyImportAction =
|
||||||
|
@ -35,36 +43,68 @@ class PGPKeyImportActivity : AppCompatActivity() {
|
||||||
contentResolver.openInputStream(uri)
|
contentResolver.openInputStream(uri)
|
||||||
?: throw IllegalStateException("Failed to open selected file")
|
?: throw IllegalStateException("Failed to open selected file")
|
||||||
val bytes = keyInputStream.use { `is` -> `is`.readBytes() }
|
val bytes = keyInputStream.use { `is` -> `is`.readBytes() }
|
||||||
val (key, error) = runBlocking { keyManager.addKey(PGPKey(bytes)) }
|
importKey(bytes, false)
|
||||||
if (error != null) throw error
|
|
||||||
key
|
|
||||||
}
|
}
|
||||||
.mapBoth(
|
.run(::handleImportResult)
|
||||||
{ key ->
|
|
||||||
if (key != null) {
|
|
||||||
MaterialAlertDialogBuilder(this)
|
|
||||||
.setTitle(getString(R.string.pgp_key_import_succeeded))
|
|
||||||
.setMessage(getString(R.string.pgp_key_import_succeeded_message, tryGetId(key)))
|
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ -> finish() }
|
|
||||||
.setOnCancelListener { finish() }
|
|
||||||
.show()
|
|
||||||
} else {
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ throwable ->
|
|
||||||
MaterialAlertDialogBuilder(this)
|
|
||||||
.setTitle(getString(R.string.pgp_key_import_failed))
|
|
||||||
.setMessage(throwable.message)
|
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ -> finish() }
|
|
||||||
.setOnCancelListener { finish() }
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
pgpKeyImportAction.launch(arrayOf("*/*"))
|
pgpKeyImportAction.launch(arrayOf("*/*"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
lastBytes = null
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun importKey(bytes: ByteArray, replace: Boolean): PGPKey? {
|
||||||
|
lastBytes = bytes
|
||||||
|
val (key, error) = runBlocking { keyManager.addKey(PGPKey(bytes), replace = replace) }
|
||||||
|
if (replace) {
|
||||||
|
lastBytes = null
|
||||||
|
}
|
||||||
|
if (error != null) throw error
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleImportResult(result: Result<PGPKey?, Throwable>) {
|
||||||
|
when (result) {
|
||||||
|
is Ok<PGPKey?> -> {
|
||||||
|
val key = result.value
|
||||||
|
if (key == null) {
|
||||||
|
finish()
|
||||||
|
// This return convinces Kotlin that the control flow for `key == null` definitely
|
||||||
|
// terminates here and allows for a smart cast below.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
MaterialAlertDialogBuilder(this)
|
||||||
|
.setTitle(getString(R.string.pgp_key_import_succeeded))
|
||||||
|
.setMessage(getString(R.string.pgp_key_import_succeeded_message, tryGetId(key)))
|
||||||
|
.setPositiveButton(android.R.string.ok) { _, _ -> finish() }
|
||||||
|
.setOnCancelListener { finish() }
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
is Err<Throwable> -> {
|
||||||
|
if (result.error is KeyAlreadyExistsException && lastBytes != null) {
|
||||||
|
MaterialAlertDialogBuilder(this)
|
||||||
|
.setTitle(getString(R.string.pgp_key_import_failed))
|
||||||
|
.setMessage(getString(R.string.pgp_key_import_failed_replace_message))
|
||||||
|
.setPositiveButton(R.string.dialog_yes) { _, _ ->
|
||||||
|
handleImportResult(runCatching { importKey(lastBytes!!, replace = true) })
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.dialog_no) { _, _ -> finish() }
|
||||||
|
.setOnCancelListener { finish() }
|
||||||
|
.show()
|
||||||
|
} else {
|
||||||
|
MaterialAlertDialogBuilder(this)
|
||||||
|
.setTitle(getString(R.string.pgp_key_import_failed))
|
||||||
|
.setMessage(result.error.message)
|
||||||
|
.setPositiveButton(android.R.string.ok) { _, _ -> finish() }
|
||||||
|
.setOnCancelListener { finish() }
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -372,6 +372,7 @@
|
||||||
<string name="place_shortcut_on_home_screen">Place shortcut on home screen</string>
|
<string name="place_shortcut_on_home_screen">Place shortcut on home screen</string>
|
||||||
<string name="password_list_fab_content_description">Create new password or folder</string>
|
<string name="password_list_fab_content_description">Create new password or folder</string>
|
||||||
<string name="pgp_key_import_failed">Failed to import PGP key</string>
|
<string name="pgp_key_import_failed">Failed to import PGP key</string>
|
||||||
|
<string name="pgp_key_import_failed_replace_message">An existing key with this ID was found, do you want to replace it?</string>
|
||||||
<string name="pgp_key_import_succeeded">Successfully imported PGP key</string>
|
<string name="pgp_key_import_succeeded">Successfully imported PGP key</string>
|
||||||
<string name="pgp_key_import_succeeded_message">The key ID of the imported key is given below, please review it for correctness:\n%1$s</string>
|
<string name="pgp_key_import_succeeded_message">The key ID of the imported key is given below, please review it for correctness:\n%1$s</string>
|
||||||
<string name="pref_category_pgp_title">PGP settings</string>
|
<string name="pref_category_pgp_title">PGP settings</string>
|
||||||
|
|
Loading…
Reference in a new issue