mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
fix: Passwordless for multiple CredentialIds
This commit is contained in:
@@ -950,14 +950,14 @@ class GroupActivity : DatabaseLockActivity(),
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun errorIfNeededForPasskeySelection(searchInfo: SearchInfo?) {
|
private fun errorIfNeededForPasskeySelection(searchInfo: SearchInfo?) {
|
||||||
if (mTypeMode == TypeMode.PASSKEY && searchInfo?.credentialId != null) {
|
if (mTypeMode == TypeMode.PASSKEY && searchInfo?.credentialIds.isNullOrEmpty().not()) {
|
||||||
removeSearch()
|
removeSearch()
|
||||||
// Build response with the entry selected
|
// Build response with the entry selected
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||||
buildPasskeyErrorAndSetResult(
|
buildPasskeyErrorAndSetResult(
|
||||||
resources = resources,
|
resources = resources,
|
||||||
relyingPartyId = searchInfo.relyingParty,
|
relyingPartyId = searchInfo.relyingParty,
|
||||||
credentialId = searchInfo.credentialId
|
credentialIds = searchInfo.credentialIds
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
onValidateSpecialMode()
|
onValidateSpecialMode()
|
||||||
|
|||||||
@@ -91,12 +91,13 @@ class PasskeyProviderService : CredentialProviderService() {
|
|||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildPasskeySearchInfo(relyingParty: String, credentialId: String? = null): SearchInfo {
|
private fun buildPasskeySearchInfo(
|
||||||
|
relyingParty: String,
|
||||||
|
credentialIds: List<String> = listOf()
|
||||||
|
): SearchInfo {
|
||||||
return SearchInfo().apply {
|
return SearchInfo().apply {
|
||||||
credentialId?.let {
|
|
||||||
this.credentialId = it
|
|
||||||
}
|
|
||||||
this.relyingParty = relyingParty
|
this.relyingParty = relyingParty
|
||||||
|
this.credentialIds = credentialIds
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,9 +146,10 @@ class PasskeyProviderService : CredentialProviderService() {
|
|||||||
|
|
||||||
val publicKeyCredentialRequestOptions = PublicKeyCredentialRequestOptions(option.requestJson)
|
val publicKeyCredentialRequestOptions = PublicKeyCredentialRequestOptions(option.requestJson)
|
||||||
val relyingPartyId = publicKeyCredentialRequestOptions.rpId
|
val relyingPartyId = publicKeyCredentialRequestOptions.rpId
|
||||||
val credentialId = publicKeyCredentialRequestOptions.allowCredentials.firstOrNull()?.id?.let { b64Encode(it) }
|
val credentialIdList = publicKeyCredentialRequestOptions.allowCredentials
|
||||||
val searchInfo = buildPasskeySearchInfo(relyingPartyId, credentialId)
|
.map { b64Encode(it.id) }
|
||||||
Log.d(TAG, "Build passkey search for relying party $relyingPartyId, credentialId $credentialId")
|
val searchInfo = buildPasskeySearchInfo(relyingPartyId, credentialIdList)
|
||||||
|
Log.d(TAG, "Build passkey search for relying party $relyingPartyId, credentialIds $credentialIdList")
|
||||||
SearchHelper.checkAutoSearchInfo(
|
SearchHelper.checkAutoSearchInfo(
|
||||||
context = this,
|
context = this,
|
||||||
database = mDatabase,
|
database = mDatabase,
|
||||||
@@ -181,7 +183,7 @@ class PasskeyProviderService : CredentialProviderService() {
|
|||||||
},
|
},
|
||||||
onItemNotFound = { _ ->
|
onItemNotFound = { _ ->
|
||||||
Log.w(TAG, "No passkey found in the database with this relying party : $relyingPartyId")
|
Log.w(TAG, "No passkey found in the database with this relying party : $relyingPartyId")
|
||||||
if (credentialId == null) {
|
if (credentialIdList.isEmpty()) {
|
||||||
Log.d(TAG, "Add pending intent for passkey selection in opened database")
|
Log.d(TAG, "Add pending intent for passkey selection in opened database")
|
||||||
PasskeyLauncherActivity.getPendingIntent(
|
PasskeyLauncherActivity.getPendingIntent(
|
||||||
context = applicationContext,
|
context = applicationContext,
|
||||||
@@ -207,7 +209,7 @@ class PasskeyProviderService : CredentialProviderService() {
|
|||||||
getString(
|
getString(
|
||||||
R.string.error_passkey_credential_id,
|
R.string.error_passkey_credential_id,
|
||||||
relyingPartyId,
|
relyingPartyId,
|
||||||
credentialId
|
credentialIdList
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,12 +207,12 @@ object PasskeyHelper {
|
|||||||
fun Activity.buildPasskeyErrorAndSetResult(
|
fun Activity.buildPasskeyErrorAndSetResult(
|
||||||
resources: Resources,
|
resources: Resources,
|
||||||
relyingPartyId: String?,
|
relyingPartyId: String?,
|
||||||
credentialId: String?
|
credentialIds: List<String>
|
||||||
) {
|
) {
|
||||||
val error = resources.getString(
|
val error = resources.getString(
|
||||||
R.string.error_passkey_credential_id,
|
R.string.error_passkey_credential_id,
|
||||||
relyingPartyId,
|
relyingPartyId,
|
||||||
credentialId
|
credentialIds
|
||||||
)
|
)
|
||||||
Log.e(javaClass.name, error)
|
Log.e(javaClass.name, error)
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ object SearchHelper {
|
|||||||
callback.invoke(
|
callback.invoke(
|
||||||
SearchParameters().apply {
|
SearchParameters().apply {
|
||||||
searchQuery = query
|
searchQuery = query
|
||||||
searchOption = optionString()
|
searchOptions = optionsString()
|
||||||
allowEmptyQuery = false
|
allowEmptyQuery = false
|
||||||
searchInTitles = false
|
searchInTitles = false
|
||||||
searchInUsernames = false
|
searchInUsernames = false
|
||||||
|
|||||||
@@ -775,5 +775,5 @@
|
|||||||
<string name="passkey_backup_eligibility">Passkey Backup Eligibility</string>
|
<string name="passkey_backup_eligibility">Passkey Backup Eligibility</string>
|
||||||
<string name="passkey_backup_state">Passkey Backup State</string>
|
<string name="passkey_backup_state">Passkey Backup State</string>
|
||||||
<string name="error_passkey_result">Unable to return the passkey</string>
|
<string name="error_passkey_result">Unable to return the passkey</string>
|
||||||
<string name="error_passkey_credential_id">No passkey found with relying party %1$s and credentialId %2$s</string>
|
<string name="error_passkey_credential_id">No passkey found with relying party %1$s and credentialIds %2$s</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -27,9 +27,9 @@ import com.kunzisoft.keepass.database.element.node.NodeId
|
|||||||
import com.kunzisoft.keepass.model.AppOriginEntryField.isAppId
|
import com.kunzisoft.keepass.model.AppOriginEntryField.isAppId
|
||||||
import com.kunzisoft.keepass.model.AppOriginEntryField.isAppIdSignature
|
import com.kunzisoft.keepass.model.AppOriginEntryField.isAppIdSignature
|
||||||
import com.kunzisoft.keepass.model.AppOriginEntryField.isWebDomain
|
import com.kunzisoft.keepass.model.AppOriginEntryField.isWebDomain
|
||||||
|
import com.kunzisoft.keepass.model.PasskeyEntryFields.isCredentialId
|
||||||
import com.kunzisoft.keepass.model.PasskeyEntryFields.isPasskey
|
import com.kunzisoft.keepass.model.PasskeyEntryFields.isPasskey
|
||||||
import com.kunzisoft.keepass.model.PasskeyEntryFields.isRelyingParty
|
import com.kunzisoft.keepass.model.PasskeyEntryFields.isRelyingParty
|
||||||
import com.kunzisoft.keepass.model.PasskeyEntryFields.isCredentialId
|
|
||||||
import com.kunzisoft.keepass.otp.OtpEntryFields.isOTP
|
import com.kunzisoft.keepass.otp.OtpEntryFields.isOTP
|
||||||
import com.kunzisoft.keepass.otp.OtpEntryFields.isOTPURIField
|
import com.kunzisoft.keepass.otp.OtpEntryFields.isOTPURIField
|
||||||
import com.kunzisoft.keepass.utils.UUIDUtils.asHexString
|
import com.kunzisoft.keepass.utils.UUIDUtils.asHexString
|
||||||
@@ -178,7 +178,7 @@ class SearchHelper {
|
|||||||
}
|
}
|
||||||
if (searchParameters.searchInRelyingParty) {
|
if (searchParameters.searchInRelyingParty) {
|
||||||
val relyingParty = searchParameters.searchQuery
|
val relyingParty = searchParameters.searchQuery
|
||||||
val credentialId = searchParameters.searchOption
|
val credentialIds = searchParameters.searchOptions
|
||||||
val containsRelyingParty = entry.getExtraFields().any { field ->
|
val containsRelyingParty = entry.getExtraFields().any { field ->
|
||||||
field.isRelyingParty()
|
field.isRelyingParty()
|
||||||
&& checkSearchQuery(
|
&& checkSearchQuery(
|
||||||
@@ -191,17 +191,20 @@ class SearchHelper {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val containsCredentialId = if(credentialId == null) true
|
// Check empty to allow any credential if not defined
|
||||||
|
val containsCredentialId = if(credentialIds.isEmpty()) true
|
||||||
else entry.getExtraFields().any { field ->
|
else entry.getExtraFields().any { field ->
|
||||||
field.isCredentialId()
|
field.isCredentialId()
|
||||||
&& checkSearchQuery(
|
&& credentialIds.any { credentialId ->
|
||||||
stringToCheck = field.protectedValue.stringValue,
|
checkSearchQuery(
|
||||||
searchParameters = SearchParameters().apply {
|
stringToCheck = field.protectedValue.stringValue,
|
||||||
searchQuery = credentialId
|
searchParameters = SearchParameters().apply {
|
||||||
caseSensitive = false
|
searchQuery = credentialId
|
||||||
isRegex = false
|
caseSensitive = false
|
||||||
}
|
isRegex = false
|
||||||
)
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return containsRelyingParty && containsCredentialId
|
return containsRelyingParty && containsCredentialId
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import android.os.Parcelable
|
|||||||
class SearchParameters() : Parcelable{
|
class SearchParameters() : Parcelable{
|
||||||
var searchQuery: String = ""
|
var searchQuery: String = ""
|
||||||
// Add an optional string to search with the main search query
|
// Add an optional string to search with the main search query
|
||||||
var searchOption: String? = null
|
var searchOptions: List<String> = listOf()
|
||||||
var allowEmptyQuery = true
|
var allowEmptyQuery = true
|
||||||
var caseSensitive = false
|
var caseSensitive = false
|
||||||
var isRegex = false
|
var isRegex = false
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class SearchInfo : ObjectNameResource, Parcelable {
|
|||||||
return if (webDomain == null) null else field
|
return if (webDomain == null) null else field
|
||||||
}
|
}
|
||||||
var relyingParty: String? = null
|
var relyingParty: String? = null
|
||||||
var credentialId: String? = null
|
var credentialIds: List<String> = listOf()
|
||||||
var otpString: String? = null
|
var otpString: String? = null
|
||||||
|
|
||||||
constructor()
|
constructor()
|
||||||
@@ -47,7 +47,7 @@ class SearchInfo : ObjectNameResource, Parcelable {
|
|||||||
webDomain = toCopy?.webDomain
|
webDomain = toCopy?.webDomain
|
||||||
webScheme = toCopy?.webScheme
|
webScheme = toCopy?.webScheme
|
||||||
relyingParty = toCopy?.relyingParty
|
relyingParty = toCopy?.relyingParty
|
||||||
credentialId = toCopy?.credentialId
|
credentialIds = toCopy?.credentialIds ?: listOf()
|
||||||
otpString = toCopy?.otpString
|
otpString = toCopy?.otpString
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,8 +63,9 @@ class SearchInfo : ObjectNameResource, Parcelable {
|
|||||||
webScheme = if (readScheme.isNullOrEmpty()) null else readScheme
|
webScheme = if (readScheme.isNullOrEmpty()) null else readScheme
|
||||||
val readRelyingParty = parcel.readString()
|
val readRelyingParty = parcel.readString()
|
||||||
relyingParty = if (readRelyingParty.isNullOrEmpty()) null else readRelyingParty
|
relyingParty = if (readRelyingParty.isNullOrEmpty()) null else readRelyingParty
|
||||||
val readCredentialId = parcel.readString()
|
val readCredentialIdList = mutableListOf<String>()
|
||||||
credentialId = if (readCredentialId.isNullOrEmpty()) null else readCredentialId
|
parcel.readStringList(readCredentialIdList)
|
||||||
|
credentialIds = readCredentialIdList.toList()
|
||||||
val readOtp = parcel.readString()
|
val readOtp = parcel.readString()
|
||||||
otpString = if (readOtp.isNullOrEmpty()) null else readOtp
|
otpString = if (readOtp.isNullOrEmpty()) null else readOtp
|
||||||
}
|
}
|
||||||
@@ -80,7 +81,7 @@ class SearchInfo : ObjectNameResource, Parcelable {
|
|||||||
parcel.writeString(webDomain ?: "")
|
parcel.writeString(webDomain ?: "")
|
||||||
parcel.writeString(webScheme ?: "")
|
parcel.writeString(webScheme ?: "")
|
||||||
parcel.writeString(relyingParty ?: "")
|
parcel.writeString(relyingParty ?: "")
|
||||||
parcel.writeString(credentialId ?: "")
|
parcel.writeStringList(credentialIds)
|
||||||
parcel.writeString(otpString ?: "")
|
parcel.writeString(otpString ?: "")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +100,7 @@ class SearchInfo : ObjectNameResource, Parcelable {
|
|||||||
&& webDomain == null
|
&& webDomain == null
|
||||||
&& webScheme == null
|
&& webScheme == null
|
||||||
&& relyingParty == null
|
&& relyingParty == null
|
||||||
&& credentialId == null
|
&& credentialIds.isEmpty()
|
||||||
&& otpString == null
|
&& otpString == null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +134,7 @@ class SearchInfo : ObjectNameResource, Parcelable {
|
|||||||
if (webDomain != other.webDomain) return false
|
if (webDomain != other.webDomain) return false
|
||||||
if (webScheme != other.webScheme) return false
|
if (webScheme != other.webScheme) return false
|
||||||
if (relyingParty != other.relyingParty) return false
|
if (relyingParty != other.relyingParty) return false
|
||||||
if (credentialId != other.credentialId) return false
|
if (credentialIds != other.credentialIds) return false
|
||||||
if (otpString != other.otpString) return false
|
if (otpString != other.otpString) return false
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@@ -146,7 +147,7 @@ class SearchInfo : ObjectNameResource, Parcelable {
|
|||||||
result = 31 * result + (webDomain?.hashCode() ?: 0)
|
result = 31 * result + (webDomain?.hashCode() ?: 0)
|
||||||
result = 31 * result + (webScheme?.hashCode() ?: 0)
|
result = 31 * result + (webScheme?.hashCode() ?: 0)
|
||||||
result = 31 * result + (relyingParty?.hashCode() ?: 0)
|
result = 31 * result + (relyingParty?.hashCode() ?: 0)
|
||||||
result = 31 * result + (credentialId?.hashCode() ?: 0)
|
result = 31 * result + (credentialIds.hashCode())
|
||||||
result = 31 * result + (otpString?.hashCode() ?: 0)
|
result = 31 * result + (otpString?.hashCode() ?: 0)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -155,8 +156,8 @@ class SearchInfo : ObjectNameResource, Parcelable {
|
|||||||
return otpString ?: webDomain ?: applicationId ?: relyingParty ?: tag ?: ""
|
return otpString ?: webDomain ?: applicationId ?: relyingParty ?: tag ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
fun optionString(): String? {
|
fun optionsString(): List<String> {
|
||||||
return if (isPasskeySearch && credentialId != null) credentialId else null
|
return if (isPasskeySearch && credentialIds.isNotEmpty()) credentialIds else listOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toRegisterInfo(): RegisterInfo {
|
fun toRegisterInfo(): RegisterInfo {
|
||||||
|
|||||||
Reference in New Issue
Block a user