Sort credential list by registrable domain
Validate Gradle Wrapper / Validation (push) Canceled after 0s

This commit is contained in:
mimi89999
2026-08-01 16:40:32 +02:00
parent 63d9a1b3dd
commit d4b12fd310
7 changed files with 16485 additions and 7 deletions
+1
View File
@@ -58,6 +58,7 @@ dependencies {
implementation(libs.androidx.credentials)
implementation(libs.kotlinx.coroutines.android)
implementation(libs.material)
implementation(libs.etldx)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
@@ -0,0 +1,34 @@
package pl.lebihan.authnkey
import pl.lebihan.etldx.InvalidDomainNameException
import pl.lebihan.etldx.PublicSuffixList
/** An rpId split into the parts we order by. Both parts are already lower cased. */
private class DomainKey(val registrable: String, val subdomain: String)
private fun PublicSuffixList.keyFor(rpId: String): DomainKey =
try {
val domain = split(rpId)
// registrableDomain is null when the rpId is a public suffix itself
DomainKey(domain.registrableDomain ?: domain.name, domain.subdomain.orEmpty())
} catch (_: InvalidDomainNameException) {
// The RP registered something that is not a domain name.
DomainKey(rpId.lowercase(), "")
}
/**
* Orders credentials by registrable domain, then by subdomain, then by user name.
*
* Sorting on the eTLD+1 keeps `example.com`, `login.example.com` and `account.example.com` together
* under E instead of scattering them across the list.
*/
fun List<CredentialItem>.sortedByRegistrableDomain(psl: PublicSuffixList): List<CredentialItem> {
val keys = map { it.rpId }.distinct().associateWith(psl::keyFor)
return sortedWith(
compareBy<CredentialItem> { keys.getValue(it.rpId).registrable }
.thenBy { keys.getValue(it.rpId).subdomain }
.thenBy(String.CASE_INSENSITIVE_ORDER) {
it.credential.userName ?: it.credential.userDisplayName ?: ""
}
)
}
@@ -861,13 +861,17 @@ class MainActivity : AppCompatActivity() {
}
}
val credentialItems = rpsWithCredentials.flatMap { rpWithCreds ->
rpWithCreds.credentials?.map { cred ->
CredentialItem(
rpId = rpWithCreds.relyingParty.rpId ?: rpWithCreds.relyingParty.rpIdHash.toHex(),
credential = cred
)
} ?: emptyList()
val credentialItems = withContext(Dispatchers.IO) {
val psl = PublicSuffixes.get(this@MainActivity)
rpsWithCredentials.flatMap { rpWithCreds ->
rpWithCreds.credentials?.map { cred ->
CredentialItem(
rpId = rpWithCreds.relyingParty.rpId
?: rpWithCreds.relyingParty.rpIdHash.toHex(),
credential = cred
)
} ?: emptyList()
}.sortedByRegistrableDomain(psl)
}
showCredentialsDialog(metadata, credentialItems)
@@ -0,0 +1,24 @@
package pl.lebihan.authnkey
import android.content.Context
import pl.lebihan.etldx.PublicSuffixList
/**
* Holds the parsed public suffix list.
*
* Parsing walks the whole list, so it is done once and the instance kept. [PublicSuffixList] is
* immutable and thread safe.
*/
object PublicSuffixes {
@Volatile
private var instance: PublicSuffixList? = null
/** Returns the list, parsing it on first use. Call from a background thread. */
fun get(context: Context): PublicSuffixList =
instance ?: synchronized(this) {
instance ?: PublicSuffixList(
context.applicationContext.resources.openRawResource(R.raw.public_suffix_list)
).also { instance = it }
}
}
File diff suppressed because it is too large Load Diff
+2
View File
@@ -9,6 +9,7 @@ appcompat = "1.7.1"
kotlinxCoroutinesAndroid = "1.11.0"
credentials = "1.6.0"
material = "1.14.0"
etldx = "0.1.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@@ -19,6 +20,7 @@ androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version
androidx-credentials = { group = "androidx.credentials", name = "credentials", version.ref = "credentials" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutinesAndroid" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
etldx = { module = "com.github.mimi89999:etldx", version.ref = "etldx" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
+4
View File
@@ -16,6 +16,10 @@ dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven {
url = uri("https://jitpack.io")
content { includeGroup("com.github.mimi89999") }
}
}
}