Add setting for fallback username (#772)

* PasswordEntry: remove useless annotations

Turns out VisibleForTesting only applies for documentation purposes. Boo >:(

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>

* PasswordEntry: silence locale warning

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>

* Add setting for fallback username

Fixes #763

Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
This commit is contained in:
Harsh Shandilya 2020-05-12 01:43:03 +05:30 committed by GitHub
parent 041cf00510
commit b16620b55c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 32 additions and 17 deletions

View file

@ -5,8 +5,6 @@
package com.zeapo.pwdstore package com.zeapo.pwdstore
import android.net.Uri import android.net.Uri
import androidx.annotation.VisibleForTesting
import androidx.annotation.VisibleForTesting.PRIVATE
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.UnsupportedEncodingException import java.io.UnsupportedEncodingException
@ -100,7 +98,7 @@ class PasswordEntry(private val content: String) {
if (line.startsWith("otpauth://totp/")) { if (line.startsWith("otpauth://totp/")) {
return Uri.parse(line).getQueryParameter("secret") return Uri.parse(line).getQueryParameter("secret")
} }
if (line.toLowerCase().startsWith("totp:")) { if (line.startsWith("totp:", ignoreCase = true)) {
return line.split(": *".toRegex(), 2).toTypedArray()[1] return line.split(": *".toRegex(), 2).toTypedArray()[1]
} }
} }
@ -165,7 +163,6 @@ class PasswordEntry(private val content: String) {
} }
companion object { companion object {
@VisibleForTesting(otherwise = PRIVATE)
val USERNAME_FIELDS = arrayOf( val USERNAME_FIELDS = arrayOf(
"login:", "login:",
"username:", "username:",

View file

@ -28,6 +28,7 @@ import androidx.core.content.edit
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import androidx.preference.CheckBoxPreference import androidx.preference.CheckBoxPreference
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
@ -114,21 +115,22 @@ class UserPreference : AppCompatActivity() {
// Autofill preferences // Autofill preferences
autoFillEnablePreference = findPreference("autofill_enable") autoFillEnablePreference = findPreference("autofill_enable")
val autoFillAppsPreference = findPreference<Preference>("autofill_apps")!! val oreoAutofillDirectoryStructurePreference = findPreference<ListPreference>("oreo_autofill_directory_structure")
val autoFillDefaultPreference = findPreference<CheckBoxPreference>("autofill_default")!! val oreoAutofillDefaultUsername = findPreference<EditTextPreference>("oreo_autofill_default_username")
val autoFillAlwaysShowDialogPreference = val autoFillAppsPreference = findPreference<Preference>("autofill_apps")
findPreference<CheckBoxPreference>("autofill_always")!! val autoFillDefaultPreference = findPreference<CheckBoxPreference>("autofill_default")
val autoFillShowFullNamePreference = val autoFillAlwaysShowDialogPreference = findPreference<CheckBoxPreference>("autofill_always")
findPreference<CheckBoxPreference>("autofill_full_path")!! val autoFillShowFullNamePreference = findPreference<CheckBoxPreference>("autofill_full_path")
autofillDependencies = listOf( autofillDependencies = listOfNotNull(
autoFillAppsPreference, autoFillAppsPreference,
autoFillDefaultPreference, autoFillDefaultPreference,
autoFillAlwaysShowDialogPreference, autoFillAlwaysShowDialogPreference,
autoFillShowFullNamePreference autoFillShowFullNamePreference
) )
val oreoAutofillDirectoryStructurePreference = oreoAutofillDependencies = listOfNotNull(
findPreference<ListPreference>("oreo_autofill_directory_structure")!! oreoAutofillDirectoryStructurePreference,
oreoAutofillDependencies = listOf(oreoAutofillDirectoryStructurePreference) oreoAutofillDefaultUsername
)
// Misc preferences // Misc preferences
val appVersionPreference = findPreference<Preference>("app_version") val appVersionPreference = findPreference<Preference>("app_version")
@ -258,7 +260,7 @@ class UserPreference : AppCompatActivity() {
selectExternalGitRepositoryPreference?.onPreferenceChangeListener = resetRepo selectExternalGitRepositoryPreference?.onPreferenceChangeListener = resetRepo
externalGitRepositoryPreference?.onPreferenceChangeListener = resetRepo externalGitRepositoryPreference?.onPreferenceChangeListener = resetRepo
autoFillAppsPreference.onPreferenceClickListener = ClickListener { autoFillAppsPreference?.onPreferenceClickListener = ClickListener {
val intent = Intent(callingActivity, AutofillPreferenceActivity::class.java) val intent = Intent(callingActivity, AutofillPreferenceActivity::class.java)
startActivity(intent) startActivity(intent)
true true

View file

@ -16,6 +16,7 @@ import android.view.autofill.AutofillId
import android.widget.RemoteViews import android.widget.RemoteViews
import android.widget.Toast import android.widget.Toast
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.preference.PreferenceManager
import com.github.ajalt.timberkt.Timber.tag import com.github.ajalt.timberkt.Timber.tag
import com.github.ajalt.timberkt.e import com.github.ajalt.timberkt.e
import com.zeapo.pwdstore.PasswordEntry import com.zeapo.pwdstore.PasswordEntry
@ -35,6 +36,12 @@ private fun ByteArray.base64(): String {
return Base64.encodeToString(this, Base64.NO_WRAP) return Base64.encodeToString(this, Base64.NO_WRAP)
} }
private fun Context.getDefaultUsername(): String? {
return PreferenceManager
.getDefaultSharedPreferences(this)
.getString("oreo_autofill_default_username", null)
}
private fun stableHash(array: Collection<ByteArray>): String { private fun stableHash(array: Collection<ByteArray>): String {
val hashes = array.map { it.sha256().base64() } val hashes = array.map { it.sha256().base64() }
return hashes.sorted().joinToString(separator = ";") return hashes.sorted().joinToString(separator = ";")
@ -82,12 +89,15 @@ val AssistStructure.ViewNode.webOrigin: String?
data class Credentials(val username: String?, val password: String) { data class Credentials(val username: String?, val password: String) {
companion object { companion object {
fun fromStoreEntry( fun fromStoreEntry(
context: Context,
file: File, file: File,
entry: PasswordEntry, entry: PasswordEntry,
directoryStructure: DirectoryStructure directoryStructure: DirectoryStructure
): Credentials { ): Credentials {
// Always give priority to a username stored in the encrypted extras // Always give priority to a username stored in the encrypted extras
val username = entry.username ?: directoryStructure.getUsernameFor(file) val username = entry.username
?: directoryStructure.getUsernameFor(file)
?: context.getDefaultUsername()
return Credentials(username, entry.password) return Credentials(username, entry.password)
} }
} }

View file

@ -180,7 +180,7 @@ class AutofillDecryptActivity : Activity(), CoroutineScope {
val entry = withContext(Dispatchers.IO) { val entry = withContext(Dispatchers.IO) {
PasswordEntry(decryptedOutput) PasswordEntry(decryptedOutput)
} }
Credentials.fromStoreEntry(file, entry, directoryStructure) Credentials.fromStoreEntry(this, file, entry, directoryStructure)
} catch (e: UnsupportedEncodingException) { } catch (e: UnsupportedEncodingException) {
e(e) { "Failed to parse password entry" } e(e) { "Failed to parse password entry" }
null null

View file

@ -370,4 +370,6 @@
<string name="snackbar_action_grant">Grant</string> <string name="snackbar_action_grant">Grant</string>
<string name="pref_debug_logging_summary">Enable debug logging (requires app restart)</string> <string name="pref_debug_logging_summary">Enable debug logging (requires app restart)</string>
<string name="pref_debug_logging_title">Debug logging</string> <string name="pref_debug_logging_title">Debug logging</string>
<string name="preference_default_username_summary">If Autofill is unable to determine a username from your password file or directory structure, it will use the value specified here</string>
<string name="preference_default_username_title">Default username</string>
</resources> </resources>

View file

@ -13,6 +13,10 @@
app:key="oreo_autofill_directory_structure" app:key="oreo_autofill_directory_structure"
app:title="@string/oreo_autofill_preference_directory_structure" app:title="@string/oreo_autofill_preference_directory_structure"
app:useSimpleSummaryProvider="true" /> app:useSimpleSummaryProvider="true" />
<EditTextPreference
app:key="oreo_autofill_default_username"
app:summary="@string/preference_default_username_summary"
app:title="@string/preference_default_username_title" />
<Preference <Preference
app:key="autofill_apps" app:key="autofill_apps"
app:title="@string/pref_autofill_apps_title" /> app:title="@string/pref_autofill_apps_title" />