aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/main/java/dev/msfjarvis/aps/util/autofill/AutofillPreferences.kt
diff options
context:
space:
mode:
authorHarsh Shandilya <me@msfjarvis.dev>2020-12-05 06:07:18 +0530
committerGitHub <noreply@github.com>2020-12-05 06:07:18 +0530
commit5e66d99c852ea67a88b650c03b0e8d55e83eccde (patch)
treeaee323a08253fe7b863976eff92a1bd412bd1430 /app/src/main/java/dev/msfjarvis/aps/util/autofill/AutofillPreferences.kt
parent8eb55f18a11d6b2f155c7c9d48ca833313ee13db (diff)
downloadHardenedPass-5e66d99c852ea67a88b650c03b0e8d55e83eccde.tar.gz
HardenedPass-5e66d99c852ea67a88b650c03b0e8d55e83eccde.zip
Refactor package structure (#1233)
* idea: default test runner to Gradle * Kick off package structure revamp * Reparent all classes under dev.msfjarvis.aps Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
Diffstat (limited to 'app/src/main/java/dev/msfjarvis/aps/util/autofill/AutofillPreferences.kt')
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/util/autofill/AutofillPreferences.kt140
1 files changed, 140 insertions, 0 deletions
diff --git a/app/src/main/java/dev/msfjarvis/aps/util/autofill/AutofillPreferences.kt b/app/src/main/java/dev/msfjarvis/aps/util/autofill/AutofillPreferences.kt
new file mode 100644
index 00000000..aa70bacb
--- /dev/null
+++ b/app/src/main/java/dev/msfjarvis/aps/util/autofill/AutofillPreferences.kt
@@ -0,0 +1,140 @@
+/*
+ * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved.
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+package dev.msfjarvis.aps.util.autofill
+
+import android.content.Context
+import android.os.Build
+import androidx.annotation.RequiresApi
+import com.github.androidpasswordstore.autofillparser.Credentials
+import dev.msfjarvis.aps.data.password.PasswordEntry
+import dev.msfjarvis.aps.util.extensions.sharedPrefs
+import dev.msfjarvis.aps.util.services.getDefaultUsername
+import java.io.File
+import java.nio.file.Paths
+
+enum class DirectoryStructure(val value: String) {
+ EncryptedUsername("encrypted_username"),
+ FileBased("file"),
+ DirectoryBased("directory");
+
+ /**
+ * Returns the username associated to [file], following the convention of the current
+ * [DirectoryStructure].
+ *
+ * Examples:
+ * - * --> null (EncryptedUsername)
+ * - work/example.org/john@doe.org.gpg --> john@doe.org (FileBased)
+ * - work/example.org/john@doe.org/password.gpg --> john@doe.org (DirectoryBased)
+ * - Temporary PIN.gpg --> Temporary PIN (DirectoryBased, fallback)
+ */
+ fun getUsernameFor(file: File): String? = when (this) {
+ EncryptedUsername -> null
+ FileBased -> file.nameWithoutExtension
+ DirectoryBased -> file.parentFile?.name ?: file.nameWithoutExtension
+ }
+
+ /**
+ * Returns the origin identifier associated to [file], following the convention of the current
+ * [DirectoryStructure].
+ *
+ * At least one of [DirectoryStructure.getIdentifierFor] and
+ * [DirectoryStructure.getAccountPartFor] will always return a non-null result.
+ *
+ * Examples:
+ * - work/example.org.gpg --> example.org (EncryptedUsername)
+ * - work/example.org/john@doe.org.gpg --> example.org (FileBased)
+ * - example.org.gpg --> example.org (FileBased, fallback)
+ * - work/example.org/john@doe.org/password.gpg --> example.org (DirectoryBased)
+ * - Temporary PIN.gpg --> null (DirectoryBased)
+ */
+ fun getIdentifierFor(file: File): String? = when (this) {
+ EncryptedUsername -> file.nameWithoutExtension
+ FileBased -> file.parentFile?.name ?: file.nameWithoutExtension
+ DirectoryBased -> file.parentFile?.parent
+ }
+
+ /**
+ * Returns the path components of [file] until right before the component that contains the
+ * origin identifier according to the current [DirectoryStructure].
+ *
+ * Examples:
+ * - work/example.org.gpg --> work (EncryptedUsername)
+ * - work/example.org/john@doe.org.gpg --> work (FileBased)
+ * - example.org/john@doe.org.gpg --> null (FileBased)
+ * - john@doe.org.gpg --> null (FileBased)
+ * - work/example.org/john@doe.org/password.gpg --> work (DirectoryBased)
+ * - example.org/john@doe.org/password.gpg --> null (DirectoryBased)
+ */
+ fun getPathToIdentifierFor(file: File): String? = when (this) {
+ EncryptedUsername -> file.parent
+ FileBased -> file.parentFile?.parent
+ DirectoryBased -> file.parentFile?.parentFile?.parent
+ }
+
+ /**
+ * Returns the path component of [file] following the origin identifier according to the current
+ * [DirectoryStructure] (without file extension).
+ *
+ * At least one of [DirectoryStructure.getIdentifierFor] and
+ * [DirectoryStructure.getAccountPartFor] will always return a non-null result.
+ *
+ * Examples:
+ * - * --> null (EncryptedUsername)
+ * - work/example.org/john@doe.org.gpg --> john@doe.org (FileBased)
+ * - example.org.gpg --> null (FileBased, fallback)
+ * - work/example.org/john@doe.org/password.gpg --> john@doe.org/password (DirectoryBased)
+ * - Temporary PIN.gpg --> Temporary PIN (DirectoryBased, fallback)
+ */
+ fun getAccountPartFor(file: File): String? = when (this) {
+ EncryptedUsername -> null
+ FileBased -> file.nameWithoutExtension.takeIf { file.parentFile != null }
+ DirectoryBased -> file.parentFile?.let { parentFile ->
+ "${parentFile.name}/${file.nameWithoutExtension}"
+ } ?: file.nameWithoutExtension
+ }
+
+ @RequiresApi(Build.VERSION_CODES.O)
+ fun getSaveFolderName(sanitizedIdentifier: String, username: String?) = when (this) {
+ EncryptedUsername -> "/"
+ FileBased -> sanitizedIdentifier
+ DirectoryBased -> Paths.get(sanitizedIdentifier, username ?: "username").toString()
+ }
+
+ fun getSaveFileName(username: String?, identifier: String) = when (this) {
+ EncryptedUsername -> identifier
+ FileBased -> username
+ DirectoryBased -> "password"
+ }
+
+ companion object {
+
+ const val PREFERENCE = "oreo_autofill_directory_structure"
+ private val DEFAULT = FileBased
+
+ private val reverseMap = values().associateBy { it.value }
+ fun fromValue(value: String?) = if (value != null) reverseMap[value] ?: DEFAULT else DEFAULT
+ }
+}
+
+object AutofillPreferences {
+
+ fun directoryStructure(context: Context): DirectoryStructure {
+ val value = context.sharedPrefs.getString(DirectoryStructure.PREFERENCE, null)
+ return DirectoryStructure.fromValue(value)
+ }
+
+ fun credentialsFromStoreEntry(
+ context: Context,
+ file: File,
+ entry: PasswordEntry,
+ directoryStructure: DirectoryStructure
+ ): Credentials {
+ // Always give priority to a username stored in the encrypted extras
+ val username = entry.username
+ ?: directoryStructure.getUsernameFor(file)
+ ?: context.getDefaultUsername()
+ return Credentials(username, entry.password, entry.calculateTotpCode())
+ }
+}