mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
fix: Passkey auto save Signature
This commit is contained in:
@@ -69,7 +69,6 @@ import com.kunzisoft.keepass.database.element.Attachment
|
||||
import com.kunzisoft.keepass.database.element.DateInstant
|
||||
import com.kunzisoft.keepass.database.element.Entry
|
||||
import com.kunzisoft.keepass.database.element.Field
|
||||
import com.kunzisoft.keepass.database.element.node.Node
|
||||
import com.kunzisoft.keepass.database.element.node.NodeId
|
||||
import com.kunzisoft.keepass.database.element.template.Template
|
||||
import com.kunzisoft.keepass.education.EntryEditActivityEducation
|
||||
@@ -81,9 +80,9 @@ import com.kunzisoft.keepass.model.SearchInfo
|
||||
import com.kunzisoft.keepass.otp.OtpElement
|
||||
import com.kunzisoft.keepass.services.AttachmentFileNotificationService
|
||||
import com.kunzisoft.keepass.services.ClipboardEntryNotificationService
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_CREATE_ENTRY_TASK
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.getNewEntry
|
||||
import com.kunzisoft.keepass.services.KeyboardEntryNotificationService
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
@@ -433,41 +432,35 @@ class EntryEditActivity : DatabaseLockActivity(),
|
||||
ACTION_DATABASE_UPDATE_ENTRY_TASK -> {
|
||||
try {
|
||||
if (result.isSuccess) {
|
||||
var newNodes: List<Node> = ArrayList()
|
||||
result.data?.getBundle(DatabaseTaskNotificationService.NEW_NODES_KEY)?.let { newNodesBundle ->
|
||||
newNodes = DatabaseTaskNotificationService.getListNodesFromBundle(database, newNodesBundle)
|
||||
}
|
||||
if (newNodes.size == 1) {
|
||||
(newNodes[0] as? Entry?)?.let { entry ->
|
||||
EntrySelectionHelper.doSpecialAction(
|
||||
intent = intent,
|
||||
defaultAction = {
|
||||
// Finish naturally
|
||||
finishForEntryResult(entry)
|
||||
},
|
||||
searchAction = {
|
||||
// Nothing when search retrieved
|
||||
},
|
||||
saveAction = {
|
||||
entryValidatedForSave(entry)
|
||||
},
|
||||
keyboardSelectionAction = {
|
||||
entryValidatedForKeyboardSelection(database, entry)
|
||||
},
|
||||
autofillSelectionAction = { _, _ ->
|
||||
entryValidatedForAutofillSelection(database, entry)
|
||||
},
|
||||
autofillRegistrationAction = {
|
||||
entryValidatedForAutofillRegistration(entry)
|
||||
},
|
||||
passkeySelectionAction = {
|
||||
entryValidatedForPasskeySelection(database, entry)
|
||||
},
|
||||
passkeyRegistrationAction = {
|
||||
entryValidatedForPasskeyRegistration(database, entry)
|
||||
}
|
||||
)
|
||||
}
|
||||
result.data?.getNewEntry(database)?.let { entry ->
|
||||
EntrySelectionHelper.doSpecialAction(
|
||||
intent = intent,
|
||||
defaultAction = {
|
||||
// Finish naturally
|
||||
finishForEntryResult(entry)
|
||||
},
|
||||
searchAction = {
|
||||
// Nothing when search retrieved
|
||||
},
|
||||
saveAction = {
|
||||
entryValidatedForSave(entry)
|
||||
},
|
||||
keyboardSelectionAction = {
|
||||
entryValidatedForKeyboardSelection(database, entry)
|
||||
},
|
||||
autofillSelectionAction = { _, _ ->
|
||||
entryValidatedForAutofillSelection(database, entry)
|
||||
},
|
||||
autofillRegistrationAction = {
|
||||
entryValidatedForAutofillRegistration(entry)
|
||||
},
|
||||
passkeySelectionAction = {
|
||||
entryValidatedForPasskeySelection(database, entry)
|
||||
},
|
||||
passkeyRegistrationAction = {
|
||||
entryValidatedForPasskeyRegistration(database, entry)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
||||
@@ -39,7 +39,6 @@ import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.viewModels
|
||||
import androidx.annotation.RequiresApi
|
||||
@@ -93,8 +92,7 @@ import com.kunzisoft.keepass.model.GroupInfo
|
||||
import com.kunzisoft.keepass.model.RegisterInfo
|
||||
import com.kunzisoft.keepass.model.SearchInfo
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.NEW_NODES_KEY
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.getListNodesFromBundle
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.getNewEntry
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.settings.SettingsActivity
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
@@ -700,9 +698,7 @@ class GroupActivity : DatabaseLockActivity(),
|
||||
|
||||
var entry: Entry? = null
|
||||
try {
|
||||
result.data?.getBundle(NEW_NODES_KEY)?.let { newNodesBundle ->
|
||||
entry = getListNodesFromBundle(database, newNodesBundle)[0] as Entry
|
||||
}
|
||||
entry = result.data?.getNewEntry(database)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to retrieve entry action for selection", e)
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import android.view.View
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.viewModels
|
||||
@@ -35,7 +35,7 @@ import androidx.lifecycle.lifecycleScope
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.activities.FileDatabaseSelectActivity
|
||||
import com.kunzisoft.keepass.activities.GroupActivity
|
||||
import com.kunzisoft.keepass.activities.legacy.DatabaseModeActivity
|
||||
import com.kunzisoft.keepass.activities.legacy.DatabaseLockActivity
|
||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.addSpecialMode
|
||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.addTypeMode
|
||||
import com.kunzisoft.keepass.credentialprovider.EntrySelectionHelper.setActivityResult
|
||||
@@ -50,11 +50,14 @@ import com.kunzisoft.keepass.credentialprovider.viewmodel.PasskeyLauncherViewMod
|
||||
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||
import com.kunzisoft.keepass.model.AppOrigin
|
||||
import com.kunzisoft.keepass.model.SearchInfo
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.ACTION_DATABASE_UPDATE_ENTRY_TASK
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import com.kunzisoft.keepass.view.toastError
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.UUID
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
||||
class PasskeyLauncherActivity : DatabaseModeActivity() {
|
||||
class PasskeyLauncherActivity : DatabaseLockActivity() {
|
||||
|
||||
private val passkeyLauncherViewModel: PasskeyLauncherViewModel by viewModels()
|
||||
|
||||
@@ -67,7 +70,7 @@ class PasskeyLauncherActivity : DatabaseModeActivity() {
|
||||
this.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
passkeyLauncherViewModel.manageRegistrationResult(it)
|
||||
}
|
||||
|
||||
|
||||
override fun applyCustomStyle(): Boolean {
|
||||
return false
|
||||
}
|
||||
@@ -76,6 +79,10 @@ class PasskeyLauncherActivity : DatabaseModeActivity() {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun finishActivityIfDatabaseNotLoaded(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
lifecycleScope.launch {
|
||||
@@ -89,14 +96,13 @@ class PasskeyLauncherActivity : DatabaseModeActivity() {
|
||||
}
|
||||
is PasskeyLauncherViewModel.UIState.ShowAppPrivilegedDialog -> {
|
||||
showAppPrivilegedDialog(
|
||||
database = uiState.database,
|
||||
temptingApp = uiState.temptingApp
|
||||
)
|
||||
}
|
||||
is PasskeyLauncherViewModel.UIState.ShowAppSignatureDialog -> {
|
||||
showAppSignatureDialog(
|
||||
database = uiState.database,
|
||||
temptingApp = uiState.temptingApp
|
||||
temptingApp = uiState.temptingApp,
|
||||
nodeId = uiState.nodeId
|
||||
)
|
||||
}
|
||||
is PasskeyLauncherViewModel.UIState.SetActivityResult -> {
|
||||
@@ -107,7 +113,8 @@ class PasskeyLauncherActivity : DatabaseModeActivity() {
|
||||
)
|
||||
}
|
||||
is PasskeyLauncherViewModel.UIState.ShowError -> {
|
||||
showError(uiState.error)
|
||||
toastError(uiState.error)
|
||||
passkeyLauncherViewModel.cancelResult()
|
||||
}
|
||||
is PasskeyLauncherViewModel.UIState.LaunchGroupActivityForSelection -> {
|
||||
GroupActivity.launchForPasskeySelectionResult(
|
||||
@@ -142,6 +149,9 @@ class PasskeyLauncherActivity : DatabaseModeActivity() {
|
||||
typeMode = uiState.typeMode
|
||||
)
|
||||
}
|
||||
is PasskeyLauncherViewModel.UIState.UpdateEntry -> {
|
||||
updateEntry(uiState.oldEntry, uiState.newEntry)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,14 +159,30 @@ class PasskeyLauncherActivity : DatabaseModeActivity() {
|
||||
|
||||
override fun onDatabaseRetrieved(database: ContextualDatabase?) {
|
||||
super.onDatabaseRetrieved(database)
|
||||
passkeyLauncherViewModel.onDatabaseRetrieved(intent, mSpecialMode, database)
|
||||
passkeyLauncherViewModel.launchPasskeyActionIfNeeded(intent, mSpecialMode, database)
|
||||
}
|
||||
|
||||
override fun onDatabaseActionFinished(
|
||||
database: ContextualDatabase,
|
||||
actionTask: String,
|
||||
result: ActionRunnable.Result
|
||||
) {
|
||||
super.onDatabaseActionFinished(database, actionTask, result)
|
||||
when (actionTask) {
|
||||
ACTION_DATABASE_UPDATE_ENTRY_TASK -> {
|
||||
passkeyLauncherViewModel.autoSelectPasskey(result, database)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun viewToInvalidateTimeout(): View? {
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a dialog that asks the user to add an app to the list of privileged apps.
|
||||
*/
|
||||
private fun showAppPrivilegedDialog(
|
||||
database: ContextualDatabase,
|
||||
temptingApp: AndroidPrivilegedApp
|
||||
) {
|
||||
Log.w(javaClass.simpleName, "No privileged apps file found")
|
||||
@@ -177,7 +203,7 @@ class PasskeyLauncherActivity : DatabaseModeActivity() {
|
||||
passkeyLauncherViewModel.saveCustomPrivilegedApp(
|
||||
intent = intent,
|
||||
specialMode = mSpecialMode,
|
||||
database = database,
|
||||
database = mDatabase,
|
||||
temptingApp = temptingApp
|
||||
)
|
||||
}
|
||||
@@ -194,8 +220,8 @@ class PasskeyLauncherActivity : DatabaseModeActivity() {
|
||||
* Display a dialog that asks the user to add an app signature in an existing passkey
|
||||
*/
|
||||
private fun showAppSignatureDialog(
|
||||
database: ContextualDatabase,
|
||||
temptingApp: AppOrigin
|
||||
temptingApp: AppOrigin,
|
||||
nodeId: UUID
|
||||
) {
|
||||
AlertDialog.Builder(this@PasskeyLauncherActivity).apply {
|
||||
setTitle(getString(R.string.passkeys_missing_signature_app_ask_title))
|
||||
@@ -203,7 +229,7 @@ class PasskeyLauncherActivity : DatabaseModeActivity() {
|
||||
.append(
|
||||
getString(
|
||||
R.string.passkeys_missing_signature_app_ask_message,
|
||||
temptingApp.toName()
|
||||
temptingApp.toString()
|
||||
)
|
||||
)
|
||||
.append("\n\n")
|
||||
@@ -212,10 +238,9 @@ class PasskeyLauncherActivity : DatabaseModeActivity() {
|
||||
)
|
||||
setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
passkeyLauncherViewModel.saveAppSignature(
|
||||
intent = intent,
|
||||
specialMode = mSpecialMode,
|
||||
database = database,
|
||||
temptingApp = temptingApp
|
||||
database = mDatabase,
|
||||
temptingApp = temptingApp,
|
||||
nodeId = nodeId
|
||||
)
|
||||
}
|
||||
setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
@@ -227,11 +252,6 @@ class PasskeyLauncherActivity : DatabaseModeActivity() {
|
||||
}.create().show()
|
||||
}
|
||||
|
||||
private fun showError(e: Throwable) {
|
||||
Log.e(TAG, "Passkey launch error", e)
|
||||
Toast.makeText(this, e.localizedMessage, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = PasskeyLauncherActivity::class.java.name
|
||||
|
||||
|
||||
@@ -24,5 +24,5 @@ import com.kunzisoft.keepass.model.AppOrigin
|
||||
data class PublicKeyCredentialUsageParameters(
|
||||
val publicKeyCredentialRequestOptions: PublicKeyCredentialRequestOptions,
|
||||
val clientDataResponse: ClientDataResponse,
|
||||
val appOrigin: AppOrigin
|
||||
var appOrigin: AppOrigin
|
||||
)
|
||||
@@ -118,11 +118,12 @@ object PasskeyHelper {
|
||||
extras: Bundle? = null
|
||||
) {
|
||||
try {
|
||||
entryInfo.passkey?.let {
|
||||
entryInfo.passkey?.let { passkey ->
|
||||
val mReplyIntent = Intent()
|
||||
Log.d(javaClass.name, "Success Passkey manual selection")
|
||||
mReplyIntent.putExtra(EXTRA_PASSKEY, entryInfo.passkey)
|
||||
mReplyIntent.putExtra(EXTRA_APP_ORIGIN, entryInfo.appOrigin)
|
||||
mReplyIntent.addPasskey(passkey)
|
||||
mReplyIntent.addAppOrigin(entryInfo.appOrigin)
|
||||
mReplyIntent.addNodeId(entryInfo.id)
|
||||
extras?.let {
|
||||
mReplyIntent.putExtras(it)
|
||||
}
|
||||
@@ -157,6 +158,15 @@ object PasskeyHelper {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the passkey to the intent
|
||||
*/
|
||||
fun Intent.addPasskey(passkey: Passkey?) {
|
||||
passkey?.let {
|
||||
putExtra(EXTRA_PASSKEY, passkey)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the passkey from the intent
|
||||
*/
|
||||
|
||||
@@ -33,14 +33,18 @@ import com.kunzisoft.keepass.credentialprovider.passkey.util.PasskeyHelper.retri
|
||||
import com.kunzisoft.keepass.credentialprovider.passkey.util.PrivilegedAllowLists
|
||||
import com.kunzisoft.keepass.credentialprovider.passkey.util.PrivilegedAllowLists.saveCustomPrivilegedApps
|
||||
import com.kunzisoft.keepass.database.ContextualDatabase
|
||||
import com.kunzisoft.keepass.database.element.Entry
|
||||
import com.kunzisoft.keepass.database.element.node.NodeIdUUID
|
||||
import com.kunzisoft.keepass.database.exception.RegisterInReadOnlyDatabaseException
|
||||
import com.kunzisoft.keepass.database.helper.SearchHelper
|
||||
import com.kunzisoft.keepass.model.AppOrigin
|
||||
import com.kunzisoft.keepass.model.Passkey
|
||||
import com.kunzisoft.keepass.model.RegisterInfo
|
||||
import com.kunzisoft.keepass.model.SearchInfo
|
||||
import com.kunzisoft.keepass.model.SignatureNotFoundException
|
||||
import com.kunzisoft.keepass.services.DatabaseTaskNotificationService.Companion.getNewEntry
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -63,7 +67,6 @@ class PasskeyLauncherViewModel(application: Application): AndroidViewModel(appli
|
||||
private var mLockDatabase: Boolean = true
|
||||
|
||||
private var isResultLauncherRegistered: Boolean = false
|
||||
private var currentDatabase: ContextualDatabase? = null
|
||||
|
||||
private val _uiState = MutableStateFlow<UIState>(UIState.Loading)
|
||||
val uiState: StateFlow<UIState> = _uiState
|
||||
@@ -74,31 +77,31 @@ class PasskeyLauncherViewModel(application: Application): AndroidViewModel(appli
|
||||
}
|
||||
|
||||
fun showAppPrivilegedDialog(
|
||||
database: ContextualDatabase,
|
||||
temptingApp: AndroidPrivilegedApp
|
||||
) {
|
||||
_uiState.value = UIState.ShowAppPrivilegedDialog(database, temptingApp)
|
||||
_uiState.value = UIState.ShowAppPrivilegedDialog(temptingApp)
|
||||
}
|
||||
|
||||
fun showAppSignatureDialog(
|
||||
database: ContextualDatabase,
|
||||
temptingApp: AppOrigin
|
||||
temptingApp: AppOrigin,
|
||||
nodeId: UUID
|
||||
) {
|
||||
_uiState.value = UIState.ShowAppSignatureDialog(database, temptingApp)
|
||||
_uiState.value = UIState.ShowAppSignatureDialog(temptingApp, nodeId)
|
||||
}
|
||||
|
||||
fun showError(error: Throwable) {
|
||||
Log.e(TAG, "Error on passkey launch", error)
|
||||
_uiState.value = UIState.ShowError(error)
|
||||
}
|
||||
|
||||
fun saveCustomPrivilegedApp(
|
||||
intent: Intent,
|
||||
specialMode: SpecialMode,
|
||||
database: ContextualDatabase,
|
||||
database: ContextualDatabase?,
|
||||
temptingApp: AndroidPrivilegedApp
|
||||
) {
|
||||
viewModelScope.launch(CoroutineExceptionHandler { _, e ->
|
||||
cancelResult()
|
||||
showError(e)
|
||||
}) {
|
||||
saveCustomPrivilegedApps(
|
||||
context = getApplication(),
|
||||
@@ -113,20 +116,39 @@ class PasskeyLauncherViewModel(application: Application): AndroidViewModel(appli
|
||||
}
|
||||
|
||||
fun saveAppSignature(
|
||||
intent: Intent,
|
||||
specialMode: SpecialMode,
|
||||
database: ContextualDatabase,
|
||||
temptingApp: AppOrigin
|
||||
database: ContextualDatabase?,
|
||||
temptingApp: AppOrigin,
|
||||
nodeId: UUID
|
||||
) {
|
||||
viewModelScope.launch(CoroutineExceptionHandler { _, e ->
|
||||
cancelResult()
|
||||
showError(e)
|
||||
}) {
|
||||
// TODO Save app signature
|
||||
// Update the entry with app signature
|
||||
val entry = database
|
||||
?.getEntryById(NodeIdUUID(nodeId))
|
||||
?: throw GetCredentialUnknownException(
|
||||
"No passkey with nodeId $nodeId found"
|
||||
)
|
||||
if (database.isReadOnly)
|
||||
throw RegisterInReadOnlyDatabaseException()
|
||||
val newEntry = Entry(entry)
|
||||
val entryInfo = newEntry.getEntryInfo(
|
||||
database,
|
||||
raw = true,
|
||||
removeTemplateConfiguration = false
|
||||
)
|
||||
entryInfo.saveAppOrigin(database, temptingApp)
|
||||
newEntry.setEntryInfo(database, entryInfo)
|
||||
_uiState.value = UIState.UpdateEntry(
|
||||
oldEntry = entry,
|
||||
newEntry = newEntry
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun setResult(intent: Intent) {
|
||||
currentDatabase = null
|
||||
// Remove the launcher register
|
||||
isResultLauncherRegistered = false
|
||||
_uiState.value = UIState.SetActivityResult(
|
||||
lockDatabase = mLockDatabase,
|
||||
resultCode = RESULT_OK,
|
||||
@@ -135,26 +157,25 @@ class PasskeyLauncherViewModel(application: Application): AndroidViewModel(appli
|
||||
}
|
||||
|
||||
fun cancelResult() {
|
||||
currentDatabase = null
|
||||
isResultLauncherRegistered = false
|
||||
_uiState.value = UIState.SetActivityResult(
|
||||
lockDatabase = mLockDatabase,
|
||||
resultCode = RESULT_CANCELED
|
||||
)
|
||||
}
|
||||
|
||||
fun onDatabaseRetrieved(
|
||||
fun launchPasskeyActionIfNeeded(
|
||||
intent: Intent,
|
||||
specialMode: SpecialMode,
|
||||
database: ContextualDatabase?
|
||||
) {
|
||||
currentDatabase = database
|
||||
if (isResultLauncherRegistered.not()) {
|
||||
isResultLauncherRegistered = true
|
||||
viewModelScope.launch(CoroutineExceptionHandler { _, e ->
|
||||
if (e is PrivilegedAllowLists.PrivilegedException && database != null) {
|
||||
showAppPrivilegedDialog(database, e.temptingApp)
|
||||
if (e is PrivilegedAllowLists.PrivilegedException) {
|
||||
showAppPrivilegedDialog(e.temptingApp)
|
||||
} else {
|
||||
showError(e)
|
||||
cancelResult()
|
||||
}
|
||||
}) {
|
||||
launchPasskeyAction(intent, specialMode, database)
|
||||
@@ -170,7 +191,6 @@ class PasskeyLauncherViewModel(application: Application): AndroidViewModel(appli
|
||||
specialMode: SpecialMode,
|
||||
database: ContextualDatabase?
|
||||
) {
|
||||
isResultLauncherRegistered = true
|
||||
val searchInfo = intent.retrieveSearchInfo() ?: SearchInfo()
|
||||
val appOrigin = intent.retrieveAppOrigin() ?: AppOrigin(verified = false)
|
||||
val nodeId = intent.retrieveNodeId()
|
||||
@@ -257,6 +277,27 @@ class PasskeyLauncherViewModel(application: Application): AndroidViewModel(appli
|
||||
}
|
||||
}
|
||||
|
||||
fun autoSelectPasskey(
|
||||
result: ActionRunnable.Result,
|
||||
database: ContextualDatabase
|
||||
) {
|
||||
viewModelScope.launch(CoroutineExceptionHandler { _, e ->
|
||||
showError(e)
|
||||
}) {
|
||||
if (result.isSuccess) {
|
||||
val entry = result.data?.getNewEntry(database)
|
||||
?: throw IOException("No passkey entry found")
|
||||
autoSelectPasskeyAndSetResult(
|
||||
database = database,
|
||||
nodeId = entry.nodeId.id,
|
||||
appOrigin = entry.getAppOrigin()
|
||||
?: throw IOException("No App origin found")
|
||||
)
|
||||
} else throw result.exception
|
||||
?: IOException("Unable to auto select passkey")
|
||||
}
|
||||
}
|
||||
|
||||
private fun autoSelectPasskeyAndSetResult(
|
||||
database: ContextualDatabase?,
|
||||
nodeId: UUID,
|
||||
@@ -268,7 +309,7 @@ class PasskeyLauncherViewModel(application: Application): AndroidViewModel(appli
|
||||
?.getEntryById(NodeIdUUID(nodeId))
|
||||
?.getEntryInfo(database)
|
||||
?.passkey
|
||||
?: throw GetCredentialUnknownException(
|
||||
?: throw IOException(
|
||||
"No passkey with nodeId $nodeId found"
|
||||
)
|
||||
// Build the response
|
||||
@@ -292,7 +333,7 @@ class PasskeyLauncherViewModel(application: Application): AndroidViewModel(appli
|
||||
setResult(result)
|
||||
} catch (e: SignatureNotFoundException) {
|
||||
// Request the dialog if signature exception
|
||||
showAppSignatureDialog(database, e.temptingApp)
|
||||
showAppSignatureDialog(e.temptingApp, nodeId)
|
||||
}
|
||||
} ?: throw IOException("Usage parameters is null")
|
||||
}
|
||||
@@ -337,21 +378,18 @@ class PasskeyLauncherViewModel(application: Application): AndroidViewModel(appli
|
||||
}
|
||||
setResult(responseIntent)
|
||||
} catch (e: SignatureNotFoundException) {
|
||||
currentDatabase?.let {
|
||||
showAppSignatureDialog(it, e.temptingApp)
|
||||
}
|
||||
intent?.retrieveNodeId()?.let { nodeId ->
|
||||
showAppSignatureDialog(e.temptingApp, nodeId)
|
||||
} ?: cancelResult()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to create selection response for passkey", e)
|
||||
showError(e)
|
||||
cancelResult()
|
||||
}
|
||||
}
|
||||
RESULT_CANCELED -> {
|
||||
cancelResult()
|
||||
}
|
||||
}
|
||||
// Remove the launcher register
|
||||
isResultLauncherRegistered = false
|
||||
}
|
||||
|
||||
// -------------
|
||||
@@ -474,12 +512,11 @@ class PasskeyLauncherViewModel(application: Application): AndroidViewModel(appli
|
||||
sealed class UIState {
|
||||
object Loading : UIState()
|
||||
data class ShowAppPrivilegedDialog(
|
||||
val database: ContextualDatabase,
|
||||
val temptingApp: AndroidPrivilegedApp
|
||||
): UIState()
|
||||
data class ShowAppSignatureDialog(
|
||||
val database: ContextualDatabase,
|
||||
val temptingApp: AppOrigin
|
||||
val temptingApp: AppOrigin,
|
||||
val nodeId: UUID
|
||||
): UIState()
|
||||
data class LaunchGroupActivityForSelection(
|
||||
val database: ContextualDatabase
|
||||
@@ -504,6 +541,10 @@ class PasskeyLauncherViewModel(application: Application): AndroidViewModel(appli
|
||||
data class ShowError(
|
||||
val error: Throwable
|
||||
): UIState()
|
||||
data class UpdateEntry(
|
||||
val oldEntry: Entry,
|
||||
val newEntry: Entry
|
||||
): UIState()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -1385,6 +1385,15 @@ open class DatabaseTaskNotificationService : LockNotificationService(), Progress
|
||||
return nodesAction
|
||||
}
|
||||
|
||||
fun Bundle.getNewEntry(database: ContextualDatabase): Entry? {
|
||||
getBundle(NEW_NODES_KEY)
|
||||
?.getParcelableList<NodeId<UUID>>(ENTRIES_ID_KEY)
|
||||
?.get(0)?.let {
|
||||
return database.getEntryById(it)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun getBundleFromListNodes(nodes: List<Node>): Bundle {
|
||||
val groupsId = mutableListOf<NodeId<*>>()
|
||||
val entriesId = mutableListOf<NodeId<UUID>>()
|
||||
|
||||
@@ -89,6 +89,14 @@ data class AppOrigin(
|
||||
} else null
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return if (androidOrigins.isNotEmpty()) {
|
||||
androidOrigins.first().toString()
|
||||
} else if (webOrigins.isNotEmpty()) {
|
||||
webOrigins.first().toString()
|
||||
} else super.toString()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val TAG = AppOrigin::class.java.simpleName
|
||||
|
||||
@@ -213,15 +213,19 @@ class EntryInfo : NodeInfo {
|
||||
registerInfo.password?.let { password = it }
|
||||
setCreditCard(registerInfo.creditCard)
|
||||
setPasskey(registerInfo.passkey)
|
||||
setAppOrigin(
|
||||
registerInfo.appOrigin,
|
||||
database?.allowEntryCustomFields() == true
|
||||
)
|
||||
saveAppOrigin(database, registerInfo.appOrigin)
|
||||
if (title.isEmpty()) {
|
||||
title = registerInfo.toString().toTitle()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add AppOrigin
|
||||
*/
|
||||
fun saveAppOrigin(database: Database?, appOrigin: AppOrigin?) {
|
||||
setAppOrigin(appOrigin, database?.allowEntryCustomFields() == true)
|
||||
}
|
||||
|
||||
fun getVisualTitle(): String {
|
||||
return title.ifEmpty {
|
||||
url.ifEmpty {
|
||||
|
||||
Reference in New Issue
Block a user