Properly support password renaming (#852)

Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
Diogenes Molinares 2020-06-14 11:31:43 +02:00 committed by GitHub
parent cd72d15b32
commit faff735a08
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 53 additions and 19 deletions

View file

@ -12,6 +12,7 @@ All notable changes to this project will be documented in this file.
- Add support for better, more secure Keyex's and MACs with a brand new SSH backend - Add support for better, more secure Keyex's and MACs with a brand new SSH backend
- Allow manually marking domains for subdomain-level association. This will allow you to keep separate passwords for `site1.example.com` and `site2.example.com` and have them show as such in Autofill. - Allow manually marking domains for subdomain-level association. This will allow you to keep separate passwords for `site1.example.com` and `site2.example.com` and have them show as such in Autofill.
- Provide better messages for OpenKeychain errors - Provide better messages for OpenKeychain errors
- Rename passwords
### Changed ### Changed
- **BREAKING**: Remove support for HOTP/TOTP secrets - Please use FIDO keys or a dedicated app like [Aegis](https://github.com/beemdevelopment/Aegis) or [andOTP](https://github.com/andOTP/andOTP) - **BREAKING**: Remove support for HOTP/TOTP secrets - Please use FIDO keys or a dedicated app like [Aegis](https://github.com/beemdevelopment/Aegis) or [andOTP](https://github.com/andOTP/andOTP)

View file

@ -12,6 +12,7 @@ import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.core.view.isVisible
import androidx.core.widget.doOnTextChanged import androidx.core.widget.doOnTextChanged
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.github.ajalt.timberkt.e import com.github.ajalt.timberkt.e
@ -29,9 +30,9 @@ import com.zeapo.pwdstore.utils.snackbar
import com.zeapo.pwdstore.utils.viewBinding import com.zeapo.pwdstore.utils.viewBinding
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.eclipse.jgit.api.Git
import me.msfjarvis.openpgpktx.util.OpenPgpApi import me.msfjarvis.openpgpktx.util.OpenPgpApi
import me.msfjarvis.openpgpktx.util.OpenPgpServiceConnection import me.msfjarvis.openpgpktx.util.OpenPgpServiceConnection
import org.eclipse.jgit.api.Git
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.File import java.io.File
@ -45,12 +46,15 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
private val suggestedPass by lazy { intent.getStringExtra(EXTRA_PASSWORD) } private val suggestedPass by lazy { intent.getStringExtra(EXTRA_PASSWORD) }
private val suggestedExtra by lazy { intent.getStringExtra(EXTRA_EXTRA_CONTENT) } private val suggestedExtra by lazy { intent.getStringExtra(EXTRA_EXTRA_CONTENT) }
private val shouldGeneratePassword by lazy { intent.getBooleanExtra(EXTRA_GENERATE_PASSWORD, false) } private val shouldGeneratePassword by lazy { intent.getBooleanExtra(EXTRA_GENERATE_PASSWORD, false) }
private val editing by lazy { intent.getBooleanExtra(EXTRA_EDITING, false) }
private val doNothing = registerForActivityResult(StartActivityForResult()) {} private val doNothing = registerForActivityResult(StartActivityForResult()) {}
private var oldCategory: String? = null
private val oldFileName by lazy { intent.getStringExtra(EXTRA_FILE_NAME) }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
bindToOpenKeychain(this, doNothing) bindToOpenKeychain(this, doNothing)
title = if (intent.getBooleanExtra(EXTRA_EDITING, false)) title = if (editing)
getString(R.string.edit_password) getString(R.string.edit_password)
else else
getString(R.string.new_password_title) getString(R.string.new_password_title)
@ -68,8 +72,10 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
// Keep empty path field visible if it is editable. // Keep empty path field visible if it is editable.
if (path.isEmpty() && !isEnabled) if (path.isEmpty() && !isEnabled)
visibility = View.GONE visibility = View.GONE
else else {
setText(path) setText(path)
oldCategory = path
}
} }
suggestedName?.let { filename.setText(it) } suggestedName?.let { filename.setText(it) }
// Allow the user to quickly switch between storing the username as the filename or // Allow the user to quickly switch between storing the username as the filename or
@ -223,6 +229,11 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
OpenPgpApi.RESULT_CODE_SUCCESS -> { OpenPgpApi.RESULT_CODE_SUCCESS -> {
try { try {
val file = File(path) val file = File(path)
// If we're not editing, this file should not already exist!
if (!editing && file.exists()) {
snackbar(message = getString(R.string.password_creation_duplicate_error))
return@executeApiAsync
}
try { try {
file.outputStream().use { file.outputStream().use {
it.write(outputStream.toByteArray()) it.write(outputStream.toByteArray())
@ -231,7 +242,7 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
e(e) { "Failed to write password file" } e(e) { "Failed to write password file" }
setResult(RESULT_CANCELED) setResult(RESULT_CANCELED)
MaterialAlertDialogBuilder(this@PasswordCreationActivity) MaterialAlertDialogBuilder(this@PasswordCreationActivity)
.setTitle(getString(R.string.password_creation_file_write_fail_title)) .setTitle(getString(R.string.password_creation_file_fail_title))
.setMessage(getString(R.string.password_creation_file_write_fail_message)) .setMessage(getString(R.string.password_creation_file_write_fail_message))
.setCancelable(false) .setCancelable(false)
.setPositiveButton(android.R.string.ok) { _, _ -> .setPositiveButton(android.R.string.ok) { _, _ ->
@ -255,20 +266,40 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB
returnIntent.putExtra(RETURN_EXTRA_USERNAME, username) returnIntent.putExtra(RETURN_EXTRA_USERNAME, username)
} }
val repo = PasswordRepository.getRepository(null) val repo = PasswordRepository.getRepository(null)
if (repo != null) { if (repo != null) {
val status = Git(repo).status().call() val status = Git(repo).status().call()
if (status.modified.isNotEmpty()) { if (status.modified.isNotEmpty()) {
commitChange( commitChange(
getString( getString(
R.string.git_commit_edit_text, R.string.git_commit_edit_text,
getLongName(fullPath, repoPath, editName) getLongName(fullPath, repoPath, editName)
) )
) )
} }
} }
setResult(RESULT_OK, returnIntent)
finish() if (category.isVisible && category.isEnabled) {
val oldFile = File("$repoPath/${oldCategory?.trim('/')}/$oldFileName.gpg")
if (oldFile.path != file.path && !oldFile.delete()) {
setResult(RESULT_CANCELED)
MaterialAlertDialogBuilder(this@PasswordCreationActivity)
.setTitle(R.string.password_creation_file_fail_title)
.setMessage(getString(R.string.password_creation_file_delete_fail_message, oldFileName))
.setCancelable(false)
.setPositiveButton(android.R.string.ok) { _, _ ->
finish()
}
.show()
} else {
setResult(RESULT_OK, returnIntent)
finish()
}
} else {
setResult(RESULT_OK, returnIntent)
finish()
}
} catch (e: Exception) { } catch (e: Exception) {
e(e) { "An Exception occurred" } e(e) { "An Exception occurred" }
} }

View file

@ -364,6 +364,8 @@
<string name="openpgp_error_unknown">Error from OpenKeyChain : %s</string> <string name="openpgp_error_unknown">Error from OpenKeyChain : %s</string>
<!-- Password creation failure --> <!-- Password creation failure -->
<string name="password_creation_file_write_fail_title">Error</string> <string name="password_creation_file_fail_title">Error</string>
<string name="password_creation_file_write_fail_message">Failed to write password file to the store, please try again.</string> <string name="password_creation_file_write_fail_message">Failed to write password file to the store, please try again.</string>
<string name="password_creation_file_delete_fail_message">Failed to delete password file %1$s from the store, please delete it manually.</string>
<string name="password_creation_duplicate_error">File already exists, please use a different name</string>
</resources> </resources>