mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
fix: Add User Verification for Entry Edition #2283
This commit is contained in:
@@ -41,6 +41,9 @@ import androidx.core.graphics.BlendModeColorFilterCompat
|
|||||||
import androidx.core.graphics.BlendModeCompat
|
import androidx.core.graphics.BlendModeCompat
|
||||||
import androidx.core.graphics.ColorUtils
|
import androidx.core.graphics.ColorUtils
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
@@ -53,6 +56,8 @@ import com.kunzisoft.keepass.activities.helpers.ExternalFileHelper
|
|||||||
import com.kunzisoft.keepass.activities.legacy.DatabaseLockActivity
|
import com.kunzisoft.keepass.activities.legacy.DatabaseLockActivity
|
||||||
import com.kunzisoft.keepass.adapters.TagsAdapter
|
import com.kunzisoft.keepass.adapters.TagsAdapter
|
||||||
import com.kunzisoft.keepass.credentialprovider.SpecialMode
|
import com.kunzisoft.keepass.credentialprovider.SpecialMode
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationData
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.askUserVerification
|
||||||
import com.kunzisoft.keepass.credentialprovider.magikeyboard.MagikeyboardService
|
import com.kunzisoft.keepass.credentialprovider.magikeyboard.MagikeyboardService
|
||||||
import com.kunzisoft.keepass.database.ContextualDatabase
|
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||||
import com.kunzisoft.keepass.database.element.Attachment
|
import com.kunzisoft.keepass.database.element.Attachment
|
||||||
@@ -79,6 +84,8 @@ import com.kunzisoft.keepass.view.hideByFading
|
|||||||
import com.kunzisoft.keepass.view.setTransparentNavigationBar
|
import com.kunzisoft.keepass.view.setTransparentNavigationBar
|
||||||
import com.kunzisoft.keepass.view.showActionErrorIfNeeded
|
import com.kunzisoft.keepass.view.showActionErrorIfNeeded
|
||||||
import com.kunzisoft.keepass.viewmodels.EntryViewModel
|
import com.kunzisoft.keepass.viewmodels.EntryViewModel
|
||||||
|
import com.kunzisoft.keepass.viewmodels.UserVerificationViewModel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import java.util.EnumSet
|
import java.util.EnumSet
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@@ -100,14 +107,10 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
private var loadingView: ProgressBar? = null
|
private var loadingView: ProgressBar? = null
|
||||||
|
|
||||||
private val mEntryViewModel: EntryViewModel by viewModels()
|
private val mEntryViewModel: EntryViewModel by viewModels()
|
||||||
|
private val mUserVerificationViewModel: UserVerificationViewModel by viewModels()
|
||||||
|
|
||||||
private val mEntryActivityEducation = EntryActivityEducation(this)
|
private val mEntryActivityEducation = EntryActivityEducation(this)
|
||||||
|
|
||||||
private var mMainEntryId: NodeId<UUID>? = null
|
|
||||||
private var mHistoryPosition: Int = -1
|
|
||||||
private var mEntryIsHistory: Boolean = false
|
|
||||||
private var mEntryLoaded = false
|
|
||||||
|
|
||||||
private var mAttachmentFileBinderManager: AttachmentFileBinderManager? = null
|
private var mAttachmentFileBinderManager: AttachmentFileBinderManager? = null
|
||||||
private var mExternalFileHelper: ExternalFileHelper? = null
|
private var mExternalFileHelper: ExternalFileHelper? = null
|
||||||
private var mAttachmentSelected: Attachment? = null
|
private var mAttachmentSelected: Attachment? = null
|
||||||
@@ -238,13 +241,9 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
|
|
||||||
mEntryViewModel.entryInfoHistory.observe(this) { entryInfoHistory ->
|
mEntryViewModel.entryInfoHistory.observe(this) { entryInfoHistory ->
|
||||||
if (entryInfoHistory != null) {
|
if (entryInfoHistory != null) {
|
||||||
this.mMainEntryId = entryInfoHistory.mainEntryId
|
|
||||||
|
|
||||||
// Manage history position
|
// Manage history position
|
||||||
val historyPosition = entryInfoHistory.historyPosition
|
val historyPosition = entryInfoHistory.historyPosition
|
||||||
this.mHistoryPosition = historyPosition
|
|
||||||
val entryIsHistory = historyPosition > -1
|
val entryIsHistory = historyPosition > -1
|
||||||
this.mEntryIsHistory = entryIsHistory
|
|
||||||
// Assign history dedicated view
|
// Assign history dedicated view
|
||||||
historyView?.visibility = if (entryIsHistory) View.VISIBLE else View.GONE
|
historyView?.visibility = if (entryIsHistory) View.VISIBLE else View.GONE
|
||||||
// TODO History badge
|
// TODO History badge
|
||||||
@@ -279,7 +278,6 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
mForegroundColor = if (showEntryColors) entryInfo.foregroundColor else null
|
mForegroundColor = if (showEntryColors) entryInfo.foregroundColor else null
|
||||||
|
|
||||||
loadingView?.hideByFading()
|
loadingView?.hideByFading()
|
||||||
mEntryLoaded = true
|
|
||||||
} else {
|
} else {
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
@@ -322,6 +320,33 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
mUserVerificationViewModel.uiState.collect { uIState ->
|
||||||
|
when (uIState) {
|
||||||
|
is UserVerificationViewModel.UIState.Loading -> {}
|
||||||
|
is UserVerificationViewModel.UIState.OnUserVerificationCanceled -> {
|
||||||
|
mUserVerificationViewModel.onUserVerificationReceived()
|
||||||
|
}
|
||||||
|
is UserVerificationViewModel.UIState.OnUserVerificationSucceeded -> {
|
||||||
|
uIState.dataToVerify.database?.let { database ->
|
||||||
|
uIState.dataToVerify.entryId?.let { entryId ->
|
||||||
|
EntryEditActivity.launch(
|
||||||
|
activity = this@EntryActivity,
|
||||||
|
database = database,
|
||||||
|
registrationType = EntryEditActivity.RegistrationType.UPDATE,
|
||||||
|
nodeId = entryId,
|
||||||
|
activityResultLauncher = mEntryActivityResultLauncher
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mUserVerificationViewModel.onUserVerificationReceived()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun finishActivityIfReloadRequested(): Boolean {
|
override fun finishActivityIfReloadRequested(): Boolean {
|
||||||
@@ -410,13 +435,13 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
super.onCreateOptionsMenu(menu)
|
super.onCreateOptionsMenu(menu)
|
||||||
if (mEntryLoaded) {
|
if (mEntryViewModel.entryLoaded) {
|
||||||
val inflater = menuInflater
|
val inflater = menuInflater
|
||||||
|
|
||||||
inflater.inflate(R.menu.entry, menu)
|
inflater.inflate(R.menu.entry, menu)
|
||||||
inflater.inflate(R.menu.database, menu)
|
inflater.inflate(R.menu.database, menu)
|
||||||
|
|
||||||
if (mEntryIsHistory && !mDatabaseReadOnly) {
|
if (mEntryViewModel.entryIsHistory && !mDatabaseReadOnly) {
|
||||||
inflater.inflate(R.menu.entry_history, menu)
|
inflater.inflate(R.menu.entry_history, menu)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,7 +454,7 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
|
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
|
||||||
if (mEntryIsHistory || mDatabaseReadOnly) {
|
if (mEntryViewModel.entryIsHistory || mDatabaseReadOnly) {
|
||||||
menu?.findItem(R.id.menu_save_database)?.isVisible = false
|
menu?.findItem(R.id.menu_save_database)?.isVisible = false
|
||||||
menu?.findItem(R.id.menu_merge_database)?.isVisible = false
|
menu?.findItem(R.id.menu_merge_database)?.isVisible = false
|
||||||
menu?.findItem(R.id.menu_edit)?.isVisible = false
|
menu?.findItem(R.id.menu_edit)?.isVisible = false
|
||||||
@@ -477,31 +502,27 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.menu_edit -> {
|
R.id.menu_edit -> {
|
||||||
mDatabase?.let { database ->
|
askUserVerification(
|
||||||
mMainEntryId?.let { entryId ->
|
userVerificationViewModel = mUserVerificationViewModel,
|
||||||
EntryEditActivity.launch(
|
userVerificationCondition = true,
|
||||||
activity = this,
|
dataToVerify = UserVerificationData(mDatabase, mEntryViewModel.mainEntryId)
|
||||||
database = database,
|
|
||||||
registrationType = EntryEditActivity.RegistrationType.UPDATE,
|
|
||||||
nodeId = entryId,
|
|
||||||
activityResultLauncher = mEntryActivityResultLauncher
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.menu_restore_entry_history -> {
|
R.id.menu_restore_entry_history -> {
|
||||||
mMainEntryId?.let { mainEntryId ->
|
mEntryViewModel.mainEntryId?.let { mainEntryId ->
|
||||||
restoreEntryHistory(
|
restoreEntryHistory(
|
||||||
mainEntryId,
|
mainEntryId,
|
||||||
mHistoryPosition)
|
mEntryViewModel.historyPosition
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
R.id.menu_delete_entry_history -> {
|
R.id.menu_delete_entry_history -> {
|
||||||
mMainEntryId?.let { mainEntryId ->
|
mEntryViewModel.mainEntryId?.let { mainEntryId ->
|
||||||
deleteEntryHistory(
|
deleteEntryHistory(
|
||||||
mainEntryId,
|
mainEntryId,
|
||||||
mHistoryPosition)
|
mEntryViewModel.historyPosition
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
R.id.menu_save_database -> {
|
R.id.menu_save_database -> {
|
||||||
@@ -521,7 +542,7 @@ class EntryActivity : DatabaseLockActivity() {
|
|||||||
override fun finish() {
|
override fun finish() {
|
||||||
// Transit data in previous Activity after an update
|
// Transit data in previous Activity after an update
|
||||||
Intent().apply {
|
Intent().apply {
|
||||||
putExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY, mMainEntryId)
|
putExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY, mEntryViewModel.mainEntryId)
|
||||||
setResult(RESULT_OK, this)
|
setResult(RESULT_OK, this)
|
||||||
}
|
}
|
||||||
super.finish()
|
super.finish()
|
||||||
|
|||||||
@@ -52,7 +52,9 @@ import androidx.core.view.WindowInsetsCompat
|
|||||||
import androidx.core.view.WindowInsetsControllerCompat
|
import androidx.core.view.WindowInsetsControllerCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.drawerlayout.widget.DrawerLayout
|
import androidx.drawerlayout.widget.DrawerLayout
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.google.android.material.datepicker.MaterialDatePicker
|
import com.google.android.material.datepicker.MaterialDatePicker
|
||||||
import com.google.android.material.timepicker.MaterialTimePicker
|
import com.google.android.material.timepicker.MaterialTimePicker
|
||||||
@@ -73,6 +75,8 @@ import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.removeModes
|
|||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveSearchInfo
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.retrieveSearchInfo
|
||||||
import com.kunzisoft.keepass.credentialprovider.SpecialMode
|
import com.kunzisoft.keepass.credentialprovider.SpecialMode
|
||||||
import com.kunzisoft.keepass.credentialprovider.TypeMode
|
import com.kunzisoft.keepass.credentialprovider.TypeMode
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationData
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.askUserVerification
|
||||||
import com.kunzisoft.keepass.credentialprovider.magikeyboard.MagikeyboardService
|
import com.kunzisoft.keepass.credentialprovider.magikeyboard.MagikeyboardService
|
||||||
import com.kunzisoft.keepass.credentialprovider.passkey.util.PasskeyHelper.buildPasskeyResponseAndSetResult
|
import com.kunzisoft.keepass.credentialprovider.passkey.util.PasskeyHelper.buildPasskeyResponseAndSetResult
|
||||||
import com.kunzisoft.keepass.database.ContextualDatabase
|
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||||
@@ -122,6 +126,7 @@ import com.kunzisoft.keepass.view.updateLockPaddingStart
|
|||||||
import com.kunzisoft.keepass.viewmodels.GroupEditViewModel
|
import com.kunzisoft.keepass.viewmodels.GroupEditViewModel
|
||||||
import com.kunzisoft.keepass.viewmodels.GroupViewModel
|
import com.kunzisoft.keepass.viewmodels.GroupViewModel
|
||||||
import com.kunzisoft.keepass.viewmodels.MainCredentialViewModel
|
import com.kunzisoft.keepass.viewmodels.MainCredentialViewModel
|
||||||
|
import com.kunzisoft.keepass.viewmodels.UserVerificationViewModel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.joda.time.LocalDateTime
|
import org.joda.time.LocalDateTime
|
||||||
import java.util.EnumSet
|
import java.util.EnumSet
|
||||||
@@ -158,6 +163,7 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
private val mGroupViewModel: GroupViewModel by viewModels()
|
private val mGroupViewModel: GroupViewModel by viewModels()
|
||||||
private val mGroupEditViewModel: GroupEditViewModel by viewModels()
|
private val mGroupEditViewModel: GroupEditViewModel by viewModels()
|
||||||
private val mMainCredentialViewModel: MainCredentialViewModel by viewModels()
|
private val mMainCredentialViewModel: MainCredentialViewModel by viewModels()
|
||||||
|
private val mUserVerificationViewModel: UserVerificationViewModel by viewModels()
|
||||||
|
|
||||||
private val mGroupActivityEducation = GroupActivityEducation(this)
|
private val mGroupActivityEducation = GroupActivityEducation(this)
|
||||||
|
|
||||||
@@ -565,6 +571,33 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
mUserVerificationViewModel.uiState.collect { uIState ->
|
||||||
|
when (uIState) {
|
||||||
|
is UserVerificationViewModel.UIState.Loading -> {}
|
||||||
|
is UserVerificationViewModel.UIState.OnUserVerificationCanceled -> {
|
||||||
|
mUserVerificationViewModel.onUserVerificationReceived()
|
||||||
|
}
|
||||||
|
is UserVerificationViewModel.UIState.OnUserVerificationSucceeded -> {
|
||||||
|
uIState.dataToVerify.database?.let { database ->
|
||||||
|
uIState.dataToVerify.entryId?.let { entryId ->
|
||||||
|
EntryEditActivity.launch(
|
||||||
|
activity = this@GroupActivity,
|
||||||
|
database = database,
|
||||||
|
registrationType = EntryEditActivity.RegistrationType.UPDATE,
|
||||||
|
nodeId = entryId,
|
||||||
|
activityResultLauncher = mEntryActivityResultLauncher
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mUserVerificationViewModel.onUserVerificationReceived()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun viewToInvalidateTimeout(): View? {
|
override fun viewToInvalidateTimeout(): View? {
|
||||||
@@ -1060,12 +1093,10 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
launchDialogForGroupUpdate(node as Group)
|
launchDialogForGroupUpdate(node as Group)
|
||||||
}
|
}
|
||||||
Type.ENTRY -> {
|
Type.ENTRY -> {
|
||||||
EntryEditActivity.launch(
|
askUserVerification(
|
||||||
activity = this@GroupActivity,
|
userVerificationViewModel = mUserVerificationViewModel,
|
||||||
database = database,
|
userVerificationCondition = true,
|
||||||
registrationType = EntryEditActivity.RegistrationType.UPDATE,
|
dataToVerify = UserVerificationData(database,node.nodeId)
|
||||||
nodeId = (node as Entry).nodeId,
|
|
||||||
activityResultLauncher = mEntryActivityResultLauncher
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.kunzisoft.keepass.credentialprovider
|
||||||
|
|
||||||
|
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||||
|
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||||
|
|
||||||
|
data class UserVerificationData(
|
||||||
|
val database: ContextualDatabase? = null,
|
||||||
|
val entryId: NodeId<*>? = null
|
||||||
|
)
|
||||||
@@ -14,7 +14,6 @@ import com.kunzisoft.keepass.R
|
|||||||
import com.kunzisoft.keepass.activities.dialogs.MainCredentialDialogFragment
|
import com.kunzisoft.keepass.activities.dialogs.MainCredentialDialogFragment
|
||||||
import com.kunzisoft.keepass.activities.dialogs.MainCredentialDialogFragment.Companion.TAG_ASK_MAIN_CREDENTIAL
|
import com.kunzisoft.keepass.activities.dialogs.MainCredentialDialogFragment.Companion.TAG_ASK_MAIN_CREDENTIAL
|
||||||
import com.kunzisoft.keepass.credentialprovider.passkey.data.UserVerificationRequirement
|
import com.kunzisoft.keepass.credentialprovider.passkey.data.UserVerificationRequirement
|
||||||
import com.kunzisoft.keepass.database.ContextualDatabase
|
|
||||||
import com.kunzisoft.keepass.utils.getEnumExtra
|
import com.kunzisoft.keepass.utils.getEnumExtra
|
||||||
import com.kunzisoft.keepass.utils.putEnumExtra
|
import com.kunzisoft.keepass.utils.putEnumExtra
|
||||||
import com.kunzisoft.keepass.view.toastError
|
import com.kunzisoft.keepass.view.toastError
|
||||||
@@ -86,12 +85,13 @@ class UserVerificationHelper {
|
|||||||
* Ask for the database credential otherwise
|
* Ask for the database credential otherwise
|
||||||
*/
|
*/
|
||||||
fun FragmentActivity.askUserVerification(
|
fun FragmentActivity.askUserVerification(
|
||||||
database: ContextualDatabase?,
|
userVerificationViewModel: UserVerificationViewModel,
|
||||||
userVerificationViewModel: UserVerificationViewModel
|
userVerificationCondition: Boolean,
|
||||||
|
dataToVerify: UserVerificationData
|
||||||
) {
|
) {
|
||||||
if (this.intent.getUserVerificationCondition()) {
|
if (userVerificationCondition) {
|
||||||
// Important to check the nullable database here
|
// Important to check the nullable database here
|
||||||
database?.let {
|
dataToVerify.database?.let {
|
||||||
if (isAuthenticatorsAllowed()) {
|
if (isAuthenticatorsAllowed()) {
|
||||||
BiometricPrompt(
|
BiometricPrompt(
|
||||||
this, ContextCompat.getMainExecutor(this),
|
this, ContextCompat.getMainExecutor(this),
|
||||||
@@ -113,20 +113,20 @@ class UserVerificationHelper {
|
|||||||
toastError(SecurityException("Authentication error: $errString"))
|
toastError(SecurityException("Authentication error: $errString"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
userVerificationViewModel.onUserVerificationFailed(database)
|
userVerificationViewModel.onUserVerificationFailed(dataToVerify)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAuthenticationSucceeded(
|
override fun onAuthenticationSucceeded(
|
||||||
result: BiometricPrompt.AuthenticationResult
|
result: BiometricPrompt.AuthenticationResult
|
||||||
) {
|
) {
|
||||||
super.onAuthenticationSucceeded(result)
|
super.onAuthenticationSucceeded(result)
|
||||||
userVerificationViewModel.onUserVerificationSucceeded(database)
|
userVerificationViewModel.onUserVerificationSucceeded(dataToVerify)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAuthenticationFailed() {
|
override fun onAuthenticationFailed() {
|
||||||
super.onAuthenticationFailed()
|
super.onAuthenticationFailed()
|
||||||
toastError(SecurityException(getString(R.string.device_unlock_not_recognized)))
|
toastError(SecurityException(getString(R.string.device_unlock_not_recognized)))
|
||||||
userVerificationViewModel.onUserVerificationFailed(database)
|
userVerificationViewModel.onUserVerificationFailed(dataToVerify)
|
||||||
}
|
}
|
||||||
}).authenticate(
|
}).authenticate(
|
||||||
BiometricPrompt.PromptInfo.Builder()
|
BiometricPrompt.PromptInfo.Builder()
|
||||||
@@ -141,7 +141,7 @@ class UserVerificationHelper {
|
|||||||
.findFragmentByTag(TAG_ASK_MAIN_CREDENTIAL) as? MainCredentialDialogFragment?
|
.findFragmentByTag(TAG_ASK_MAIN_CREDENTIAL) as? MainCredentialDialogFragment?
|
||||||
if (mainCredentialDialogFragment == null) {
|
if (mainCredentialDialogFragment == null) {
|
||||||
mainCredentialDialogFragment = MainCredentialDialogFragment
|
mainCredentialDialogFragment = MainCredentialDialogFragment
|
||||||
.getInstance(database.fileUri)
|
.getInstance(dataToVerify.database.fileUri)
|
||||||
mainCredentialDialogFragment.show(
|
mainCredentialDialogFragment.show(
|
||||||
supportFragmentManager,
|
supportFragmentManager,
|
||||||
TAG_ASK_MAIN_CREDENTIAL
|
TAG_ASK_MAIN_CREDENTIAL
|
||||||
@@ -150,7 +150,7 @@ class UserVerificationHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
userVerificationViewModel.onUserVerificationSucceeded(database)
|
userVerificationViewModel.onUserVerificationSucceeded(dataToVerify)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,9 @@ import androidx.activity.result.contract.ActivityResultContracts
|
|||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.activities.FileDatabaseSelectActivity
|
import com.kunzisoft.keepass.activities.FileDatabaseSelectActivity
|
||||||
import com.kunzisoft.keepass.activities.GroupActivity
|
import com.kunzisoft.keepass.activities.GroupActivity
|
||||||
@@ -43,6 +45,7 @@ import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.addTypeMode
|
|||||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.setActivityResult
|
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.setActivityResult
|
||||||
import com.kunzisoft.keepass.credentialprovider.SpecialMode
|
import com.kunzisoft.keepass.credentialprovider.SpecialMode
|
||||||
import com.kunzisoft.keepass.credentialprovider.TypeMode
|
import com.kunzisoft.keepass.credentialprovider.TypeMode
|
||||||
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationData
|
||||||
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.addUserVerification
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.addUserVerification
|
||||||
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.askUserVerification
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.askUserVerification
|
||||||
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.getUserVerificationCondition
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationHelper.Companion.getUserVerificationCondition
|
||||||
@@ -186,6 +189,7 @@ class PasskeyLauncherActivity : DatabaseLockActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
userVerificationViewModel.uiState.collect { uiState ->
|
userVerificationViewModel.uiState.collect { uiState ->
|
||||||
when (uiState) {
|
when (uiState) {
|
||||||
is UserVerificationViewModel.UIState.Loading -> {}
|
is UserVerificationViewModel.UIState.Loading -> {}
|
||||||
@@ -194,11 +198,14 @@ class PasskeyLauncherActivity : DatabaseLockActivity() {
|
|||||||
userVerified = true,
|
userVerified = true,
|
||||||
intent = intent,
|
intent = intent,
|
||||||
specialMode = mSpecialMode,
|
specialMode = mSpecialMode,
|
||||||
database = uiState.database
|
database = uiState.dataToVerify.database
|
||||||
)
|
)
|
||||||
|
userVerificationViewModel.onUserVerificationReceived()
|
||||||
}
|
}
|
||||||
is UserVerificationViewModel.UIState.OnUserVerificationCanceled -> {
|
is UserVerificationViewModel.UIState.OnUserVerificationCanceled -> {
|
||||||
passkeyLauncherViewModel.cancelResult()
|
passkeyLauncherViewModel.cancelResult()
|
||||||
|
userVerificationViewModel.onUserVerificationReceived()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,9 +215,11 @@ class PasskeyLauncherActivity : DatabaseLockActivity() {
|
|||||||
override fun onUnknownDatabaseRetrieved(database: ContextualDatabase?) {
|
override fun onUnknownDatabaseRetrieved(database: ContextualDatabase?) {
|
||||||
super.onUnknownDatabaseRetrieved(database)
|
super.onUnknownDatabaseRetrieved(database)
|
||||||
// To manage https://github.com/Kunzisoft/KeePassDX/issues/2283
|
// To manage https://github.com/Kunzisoft/KeePassDX/issues/2283
|
||||||
|
// When a database is opened
|
||||||
askUserVerification(
|
askUserVerification(
|
||||||
database = database,
|
userVerificationViewModel = userVerificationViewModel,
|
||||||
userVerificationViewModel = userVerificationViewModel
|
userVerificationCondition = intent.getUserVerificationCondition(),
|
||||||
|
dataToVerify = UserVerificationData(database)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,9 +236,13 @@ class PasskeyLauncherActivity : DatabaseLockActivity() {
|
|||||||
}
|
}
|
||||||
ACTION_DATABASE_CHECK_CREDENTIAL_TASK -> {
|
ACTION_DATABASE_CHECK_CREDENTIAL_TASK -> {
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
userVerificationViewModel.onUserVerificationSucceeded(database)
|
userVerificationViewModel.onUserVerificationSucceeded(
|
||||||
|
UserVerificationData(database)
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
userVerificationViewModel.onUserVerificationFailed(database)
|
userVerificationViewModel.onUserVerificationFailed(
|
||||||
|
UserVerificationData(database)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,8 +36,14 @@ import java.util.UUID
|
|||||||
|
|
||||||
class EntryViewModel: ViewModel() {
|
class EntryViewModel: ViewModel() {
|
||||||
|
|
||||||
private var mMainEntryId: NodeId<UUID>? = null
|
var mainEntryId: NodeId<UUID>? = null
|
||||||
private var mHistoryPosition: Int = -1
|
private set
|
||||||
|
var historyPosition: Int = -1
|
||||||
|
private set
|
||||||
|
var entryIsHistory: Boolean = false
|
||||||
|
private set
|
||||||
|
var entryLoaded = false
|
||||||
|
private set
|
||||||
|
|
||||||
val entryInfoHistory : LiveData<EntryInfoHistory?> get() = _entryInfoHistory
|
val entryInfoHistory : LiveData<EntryInfoHistory?> get() = _entryInfoHistory
|
||||||
private val _entryInfoHistory = MutableLiveData<EntryInfoHistory?>()
|
private val _entryInfoHistory = MutableLiveData<EntryInfoHistory?>()
|
||||||
@@ -60,12 +66,12 @@ class EntryViewModel: ViewModel() {
|
|||||||
private val _historySelected = SingleLiveEvent<EntryHistory>()
|
private val _historySelected = SingleLiveEvent<EntryHistory>()
|
||||||
|
|
||||||
fun loadDatabase(database: ContextualDatabase?) {
|
fun loadDatabase(database: ContextualDatabase?) {
|
||||||
loadEntry(database, mMainEntryId, mHistoryPosition)
|
loadEntry(database, mainEntryId, historyPosition)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun loadEntry(database: ContextualDatabase?, mainEntryId: NodeId<UUID>?, historyPosition: Int = -1) {
|
fun loadEntry(database: ContextualDatabase?, mainEntryId: NodeId<UUID>?, historyPosition: Int = -1) {
|
||||||
this.mMainEntryId = mainEntryId
|
this.mainEntryId = mainEntryId
|
||||||
this.mHistoryPosition = historyPosition
|
this.historyPosition = historyPosition
|
||||||
|
|
||||||
if (database != null && mainEntryId != null) {
|
if (database != null && mainEntryId != null) {
|
||||||
IOActionTask(
|
IOActionTask(
|
||||||
@@ -104,6 +110,12 @@ class EntryViewModel: ViewModel() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ entryInfoHistory ->
|
{ entryInfoHistory ->
|
||||||
|
if (entryInfoHistory != null) {
|
||||||
|
this.mainEntryId = entryInfoHistory.mainEntryId
|
||||||
|
this.historyPosition = historyPosition
|
||||||
|
this.entryIsHistory = historyPosition > -1
|
||||||
|
this.entryLoaded = true
|
||||||
|
}
|
||||||
_entryInfoHistory.value = entryInfoHistory
|
_entryInfoHistory.value = entryInfoHistory
|
||||||
_entryHistory.value = entryInfoHistory?.entryHistory
|
_entryHistory.value = entryInfoHistory?.entryHistory
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.kunzisoft.keepass.viewmodels
|
package com.kunzisoft.keepass.viewmodels
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import com.kunzisoft.keepass.database.ContextualDatabase
|
import com.kunzisoft.keepass.credentialprovider.UserVerificationData
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
@@ -13,22 +13,22 @@ class UserVerificationViewModel: ViewModel() {
|
|||||||
private val mUiState = MutableStateFlow<UIState>(UIState.Loading)
|
private val mUiState = MutableStateFlow<UIState>(UIState.Loading)
|
||||||
val uiState: StateFlow<UIState> = mUiState
|
val uiState: StateFlow<UIState> = mUiState
|
||||||
|
|
||||||
fun onUserVerificationSucceeded(database: ContextualDatabase?) {
|
fun onUserVerificationSucceeded(dataToVerify: UserVerificationData) {
|
||||||
mUiState.value = UIState.OnUserVerificationSucceeded(database)
|
mUiState.value = UIState.OnUserVerificationSucceeded(dataToVerify)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onUserVerificationFailed(database: ContextualDatabase? = null) {
|
fun onUserVerificationFailed(dataToVerify: UserVerificationData = UserVerificationData()) {
|
||||||
mUiState.value = UIState.OnUserVerificationCanceled(database)
|
mUiState.value = UIState.OnUserVerificationCanceled(dataToVerify)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onUserVerificationReceived() {
|
||||||
|
mUiState.value = UIState.Loading
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class UIState {
|
sealed class UIState {
|
||||||
object Loading: UIState()
|
object Loading: UIState()
|
||||||
data class OnUserVerificationSucceeded(
|
data class OnUserVerificationSucceeded(val dataToVerify: UserVerificationData): UIState()
|
||||||
val database: ContextualDatabase?
|
data class OnUserVerificationCanceled(val dataToVerify: UserVerificationData): UIState()
|
||||||
): UIState()
|
|
||||||
data class OnUserVerificationCanceled(
|
|
||||||
val database: ContextualDatabase?
|
|
||||||
): UIState()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user