add checkbox in passphrase dialog to clear cache (#3127)
* add checkbox in passphrase dialog to clear cache * instantiating PasswordDialog via newInstance, passing args as Bundle * refactor: put checkbox directly in the layout --------- Co-authored-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
parent
457ecade9c
commit
080387ce75
5 changed files with 60 additions and 7 deletions
|
@ -11,6 +11,7 @@ import android.content.IntentSender
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.autofill.AutofillManager
|
import android.view.autofill.AutofillManager
|
||||||
|
import androidx.core.content.edit
|
||||||
import androidx.fragment.app.setFragmentResultListener
|
import androidx.fragment.app.setFragmentResultListener
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import app.passwordstore.Application.Companion.screenWasOff
|
import app.passwordstore.Application.Companion.screenWasOff
|
||||||
|
@ -53,6 +54,7 @@ class AutofillDecryptActivity : BasePGPActivity() {
|
||||||
@Inject lateinit var passphraseCache: PGPPassphraseCache
|
@Inject lateinit var passphraseCache: PGPPassphraseCache
|
||||||
|
|
||||||
private lateinit var directoryStructure: DirectoryStructure
|
private lateinit var directoryStructure: DirectoryStructure
|
||||||
|
private var clearCache = true
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
|
@ -114,7 +116,8 @@ class AutofillDecryptActivity : BasePGPActivity() {
|
||||||
is Result.Success -> {
|
is Result.Success -> {
|
||||||
/* clear passphrase cache on first use after application startup or if screen was off;
|
/* clear passphrase cache on first use after application startup or if screen was off;
|
||||||
also make sure to purge a stale cache after caching has been disabled via PGP settings */
|
also make sure to purge a stale cache after caching has been disabled via PGP settings */
|
||||||
if (screenWasOff && settings.getBoolean(PreferenceKeys.CLEAR_PASSPHRASE_CACHE, true)) {
|
clearCache = settings.getBoolean(PreferenceKeys.CLEAR_PASSPHRASE_CACHE, true)
|
||||||
|
if (screenWasOff && clearCache) {
|
||||||
passphraseCache.clearAllCachedPassphrases(this@AutofillDecryptActivity)
|
passphraseCache.clearAllCachedPassphrases(this@AutofillDecryptActivity)
|
||||||
screenWasOff = false
|
screenWasOff = false
|
||||||
}
|
}
|
||||||
|
@ -149,11 +152,16 @@ class AutofillDecryptActivity : BasePGPActivity() {
|
||||||
decryptWithPassphrase(File(filePath), identifiers, clientState, action, password = "")
|
decryptWithPassphrase(File(filePath), identifiers, clientState, action, password = "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val dialog = PasswordDialog()
|
val dialog =
|
||||||
|
PasswordDialog.newInstance(
|
||||||
|
cacheEnabled = features.isEnabled(EnablePGPPassphraseCache),
|
||||||
|
clearCache = clearCache,
|
||||||
|
)
|
||||||
dialog.show(supportFragmentManager, "PASSWORD_DIALOG")
|
dialog.show(supportFragmentManager, "PASSWORD_DIALOG")
|
||||||
dialog.setFragmentResultListener(PasswordDialog.PASSWORD_RESULT_KEY) { key, bundle ->
|
dialog.setFragmentResultListener(PasswordDialog.PASSWORD_RESULT_KEY) { key, bundle ->
|
||||||
if (key == PasswordDialog.PASSWORD_RESULT_KEY) {
|
if (key == PasswordDialog.PASSWORD_RESULT_KEY) {
|
||||||
val value = bundle.getString(PasswordDialog.PASSWORD_RESULT_KEY)!!
|
val value = bundle.getString(PasswordDialog.PASSWORD_PHRASE_KEY)!!
|
||||||
|
clearCache = bundle.getBoolean(PasswordDialog.PASSWORD_CLEAR_KEY)
|
||||||
lifecycleScope.launch(dispatcherProvider.main()) {
|
lifecycleScope.launch(dispatcherProvider.main()) {
|
||||||
decryptWithPassphrase(File(filePath), identifiers, clientState, action, value)
|
decryptWithPassphrase(File(filePath), identifiers, clientState, action, value)
|
||||||
}
|
}
|
||||||
|
@ -185,6 +193,8 @@ class AutofillDecryptActivity : BasePGPActivity() {
|
||||||
Intent().apply { putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, fillInDataset) },
|
Intent().apply { putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, fillInDataset) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (features.isEnabled(EnablePGPPassphraseCache))
|
||||||
|
settings.edit { putBoolean(PreferenceKeys.CLEAR_PASSPHRASE_CACHE, clearCache) }
|
||||||
}
|
}
|
||||||
withContext(dispatcherProvider.main()) { finish() }
|
withContext(dispatcherProvider.main()) { finish() }
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
|
import androidx.core.content.edit
|
||||||
import androidx.fragment.app.setFragmentResultListener
|
import androidx.fragment.app.setFragmentResultListener
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import app.passwordstore.Application.Companion.screenWasOff
|
import app.passwordstore.Application.Companion.screenWasOff
|
||||||
|
@ -56,6 +57,7 @@ class DecryptActivity : BasePGPActivity() {
|
||||||
private val relativeParentPath by unsafeLazy { getParentPath(fullPath, repoPath) }
|
private val relativeParentPath by unsafeLazy { getParentPath(fullPath, repoPath) }
|
||||||
private var passwordEntry: PasswordEntry? = null
|
private var passwordEntry: PasswordEntry? = null
|
||||||
private var retries = 0
|
private var retries = 0
|
||||||
|
private var clearCache = true
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
@ -170,7 +172,8 @@ class DecryptActivity : BasePGPActivity() {
|
||||||
is BiometricResult.Success -> {
|
is BiometricResult.Success -> {
|
||||||
/* clear passphrase cache on first use after application startup or if screen was off;
|
/* clear passphrase cache on first use after application startup or if screen was off;
|
||||||
also make sure to purge a stale cache after caching has been disabled via PGP settings */
|
also make sure to purge a stale cache after caching has been disabled via PGP settings */
|
||||||
if (screenWasOff && settings.getBoolean(PreferenceKeys.CLEAR_PASSPHRASE_CACHE, true)) {
|
clearCache = settings.getBoolean(PreferenceKeys.CLEAR_PASSPHRASE_CACHE, true)
|
||||||
|
if (screenWasOff && clearCache) {
|
||||||
passphraseCache.clearAllCachedPassphrases(this@DecryptActivity)
|
passphraseCache.clearAllCachedPassphrases(this@DecryptActivity)
|
||||||
screenWasOff = false
|
screenWasOff = false
|
||||||
}
|
}
|
||||||
|
@ -200,14 +203,19 @@ class DecryptActivity : BasePGPActivity() {
|
||||||
decryptWithPassphrase(passphrase = "", gpgIdentifiers, authResult)
|
decryptWithPassphrase(passphrase = "", gpgIdentifiers, authResult)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val dialog = PasswordDialog()
|
val dialog =
|
||||||
|
PasswordDialog.newInstance(
|
||||||
|
cacheEnabled = features.isEnabled(EnablePGPPassphraseCache),
|
||||||
|
clearCache = clearCache,
|
||||||
|
)
|
||||||
if (isError) {
|
if (isError) {
|
||||||
dialog.setError()
|
dialog.setError()
|
||||||
}
|
}
|
||||||
dialog.show(supportFragmentManager, "PASSWORD_DIALOG")
|
dialog.show(supportFragmentManager, "PASSWORD_DIALOG")
|
||||||
dialog.setFragmentResultListener(PasswordDialog.PASSWORD_RESULT_KEY) { key, bundle ->
|
dialog.setFragmentResultListener(PasswordDialog.PASSWORD_RESULT_KEY) { key, bundle ->
|
||||||
if (key == PasswordDialog.PASSWORD_RESULT_KEY) {
|
if (key == PasswordDialog.PASSWORD_RESULT_KEY) {
|
||||||
val passphrase = bundle.getString(PasswordDialog.PASSWORD_RESULT_KEY)!!
|
val passphrase = bundle.getString(PasswordDialog.PASSWORD_PHRASE_KEY)!!
|
||||||
|
clearCache = bundle.getBoolean(PasswordDialog.PASSWORD_CLEAR_KEY)
|
||||||
lifecycleScope.launch(dispatcherProvider.main()) {
|
lifecycleScope.launch(dispatcherProvider.main()) {
|
||||||
decryptWithPassphrase(passphrase, gpgIdentifiers, authResult) {
|
decryptWithPassphrase(passphrase, gpgIdentifiers, authResult) {
|
||||||
if (authResult is BiometricResult.Success) {
|
if (authResult is BiometricResult.Success) {
|
||||||
|
@ -231,6 +239,8 @@ class DecryptActivity : BasePGPActivity() {
|
||||||
) {
|
) {
|
||||||
val result = decryptPGPStream(passphrase, identifiers)
|
val result = decryptPGPStream(passphrase, identifiers)
|
||||||
if (result.isOk) {
|
if (result.isOk) {
|
||||||
|
if (features.isEnabled(EnablePGPPassphraseCache))
|
||||||
|
settings.edit { putBoolean(PreferenceKeys.CLEAR_PASSPHRASE_CACHE, clearCache) }
|
||||||
val entry = passwordEntryFactory.create(result.value.toByteArray())
|
val entry = passwordEntryFactory.create(result.value.toByteArray())
|
||||||
passwordEntry = entry
|
passwordEntry = entry
|
||||||
createPasswordUI(entry)
|
createPasswordUI(entry)
|
||||||
|
|
|
@ -25,11 +25,21 @@ class PasswordDialog : DialogFragment() {
|
||||||
|
|
||||||
private val binding by unsafeLazy { DialogPasswordEntryBinding.inflate(layoutInflater) }
|
private val binding by unsafeLazy { DialogPasswordEntryBinding.inflate(layoutInflater) }
|
||||||
private var isError: Boolean = false
|
private var isError: Boolean = false
|
||||||
|
private var clearCacheChecked: Boolean = true
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
val builder = MaterialAlertDialogBuilder(requireContext())
|
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||||
builder.setView(binding.root)
|
builder.setView(binding.root)
|
||||||
builder.setTitle(R.string.password)
|
builder.setTitle(R.string.password)
|
||||||
|
|
||||||
|
if (requireArguments().getBoolean(CACHE_ENABLED_EXTRA, false)) {
|
||||||
|
clearCacheChecked = requireArguments().getBoolean(AUTO_CLEAR_CACHE_EXTRA)
|
||||||
|
binding.autoClearCache.isChecked = clearCacheChecked
|
||||||
|
binding.autoClearCache.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
clearCacheChecked = isChecked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
builder.setPositiveButton(android.R.string.ok) { _, _ -> setPasswordAndDismiss() }
|
builder.setPositiveButton(android.R.string.ok) { _, _ -> setPasswordAndDismiss() }
|
||||||
val dialog = builder.create()
|
val dialog = builder.create()
|
||||||
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
|
||||||
|
@ -64,11 +74,28 @@ class PasswordDialog : DialogFragment() {
|
||||||
|
|
||||||
private fun setPasswordAndDismiss() {
|
private fun setPasswordAndDismiss() {
|
||||||
val password = binding.passwordEditText.text.toString()
|
val password = binding.passwordEditText.text.toString()
|
||||||
setFragmentResult(PASSWORD_RESULT_KEY, bundleOf(PASSWORD_RESULT_KEY to password))
|
setFragmentResult(
|
||||||
|
PASSWORD_RESULT_KEY,
|
||||||
|
bundleOf(PASSWORD_PHRASE_KEY to password, PASSWORD_CLEAR_KEY to clearCacheChecked),
|
||||||
|
)
|
||||||
dismissAllowingStateLoss()
|
dismissAllowingStateLoss()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
private const val CACHE_ENABLED_EXTRA = "CACHE_ENABLED"
|
||||||
|
private const val AUTO_CLEAR_CACHE_EXTRA = "AUTO_CLEAR_CACHE"
|
||||||
|
|
||||||
const val PASSWORD_RESULT_KEY = "password_result"
|
const val PASSWORD_RESULT_KEY = "password_result"
|
||||||
|
const val PASSWORD_PHRASE_KEY = "password_phrase"
|
||||||
|
const val PASSWORD_CLEAR_KEY = "password_clear"
|
||||||
|
|
||||||
|
fun newInstance(cacheEnabled: Boolean, clearCache: Boolean): PasswordDialog {
|
||||||
|
val extras =
|
||||||
|
bundleOf(CACHE_ENABLED_EXTRA to cacheEnabled, AUTO_CLEAR_CACHE_EXTRA to clearCache)
|
||||||
|
val fragment = PasswordDialog()
|
||||||
|
fragment.arguments = extras
|
||||||
|
return fragment
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,4 +27,9 @@
|
||||||
<requestFocus />
|
<requestFocus />
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.checkbox.MaterialCheckBox
|
||||||
|
android:id="@+id/auto_clear_cache"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:text="@string/clear_cached_password_on_screen_off"
|
||||||
|
android:layout_height="wrap_content" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
@ -381,4 +381,5 @@
|
||||||
<string name="biometric_prompt_title_gpg_passphrase_cache">Unlock passphrase cache</string>
|
<string name="biometric_prompt_title_gpg_passphrase_cache">Unlock passphrase cache</string>
|
||||||
<string name="aead_detect_title">AEAD encryption detected</string>
|
<string name="aead_detect_title">AEAD encryption detected</string>
|
||||||
<string name="aead_detect_message">%1$s, see https://passwordstore.app/fix-aead for more information</string>
|
<string name="aead_detect_message">%1$s, see https://passwordstore.app/fix-aead for more information</string>
|
||||||
|
<string name="clear_cached_password_on_screen_off">Clear cached password on screen-off</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in a new issue