From 15aa92980239d6d61c3b0febc0407b7f63e152b3 Mon Sep 17 00:00:00 2001 From: Fabian Henneke Date: Tue, 11 Aug 2020 18:11:39 +0200 Subject: [PATCH] Switch to URL-based Git config and refactor Git settings (#1008) * Make Git config URL-based and refactor * Use Kotlin style null handling for string prefs * Also show an error if generated URL can't be parsed * Add some testcases for migration strategy Signed-off-by: Harsh Shandilya Co-authored-by: Harsh Shandilya --- CHANGELOG.md | 1 + .../java/com/zeapo/pwdstore/MigrationsTest.kt | 81 +++++++++++ .../git/GitServerConfigActivityTest.kt | 126 ------------------ .../java/com/zeapo/pwdstore/Application.kt | 5 +- .../com/zeapo/pwdstore/ClipboardService.kt | 4 +- .../java/com/zeapo/pwdstore/LaunchActivity.kt | 1 - .../java/com/zeapo/pwdstore/Migrations.kt | 83 ++++++++++++ .../com/zeapo/pwdstore/PasswordFragment.kt | 5 +- .../java/com/zeapo/pwdstore/PasswordStore.kt | 15 +-- .../pwdstore/SearchableRepositoryViewModel.kt | 1 - .../java/com/zeapo/pwdstore/UserPreference.kt | 20 +-- .../pwdstore/autofill/oreo/AutofillHelper.kt | 8 +- .../autofill/oreo/AutofillPreferences.kt | 2 - .../autofill/oreo/PublicSuffixListCache.kt | 9 +- .../zeapo/pwdstore/crypto/BasePgpActivity.kt | 4 +- .../crypto/PasswordCreationActivity.kt | 3 +- .../com/zeapo/pwdstore/git/BaseGitActivity.kt | 122 +---------------- .../zeapo/pwdstore/git/GitConfigActivity.kt | 21 ++- .../pwdstore/git/GitOperationActivity.kt | 5 +- .../pwdstore/git/GitServerConfigActivity.kt | 91 +++---------- .../pwdstore/git/config/ConnectionMode.kt | 22 --- .../zeapo/pwdstore/git/config/GitSettings.kt | 115 ++++++++++++++++ .../com/zeapo/pwdstore/git/config/Protocol.kt | 20 --- .../git/config/SshApiSessionFactory.java | 36 +---- .../pwdstore/git/config/SshjSessionFactory.kt | 4 +- .../pwdstore/git/operation/GitOperation.kt | 33 +++-- .../pwdstore/pwgenxkpwd/XkpwdDictionary.kt | 4 +- .../pwdstore/sshkeygen/SshKeyGenActivity.kt | 1 - .../adapters/PasswordItemRecyclerAdapter.kt | 1 - .../com/zeapo/pwdstore/utils/Extensions.kt | 2 + .../pwdstore/utils/PasswordRepository.kt | 9 +- .../zeapo/pwdstore/utils/PreferenceKeys.kt | 9 +- .../main/res/layout/activity_git_clone.xml | 68 +--------- app/src/main/res/values-ar/strings.xml | 4 - app/src/main/res/values-cs/strings.xml | 4 - app/src/main/res/values-de/strings.xml | 4 - app/src/main/res/values-es/strings.xml | 4 - app/src/main/res/values-fr/strings.xml | 4 - app/src/main/res/values-ja/strings.xml | 4 - app/src/main/res/values-pt-rBR/strings.xml | 5 - app/src/main/res/values-ru/strings.xml | 4 - app/src/main/res/values-zh-rCN/strings.xml | 4 - app/src/main/res/values-zh-rTW/strings.xml | 4 - app/src/main/res/values/strings.xml | 8 +- 44 files changed, 395 insertions(+), 585 deletions(-) create mode 100644 app/src/androidTest/java/com/zeapo/pwdstore/MigrationsTest.kt delete mode 100644 app/src/androidTest/java/com/zeapo/pwdstore/git/GitServerConfigActivityTest.kt create mode 100644 app/src/main/java/com/zeapo/pwdstore/Migrations.kt delete mode 100644 app/src/main/java/com/zeapo/pwdstore/git/config/ConnectionMode.kt create mode 100644 app/src/main/java/com/zeapo/pwdstore/git/config/GitSettings.kt delete mode 100644 app/src/main/java/com/zeapo/pwdstore/git/config/Protocol.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 418ad027..2b2249a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file. ### Changed +- The Git repository URL can now be specified directly - Slightly reduce APK size - Always show the parent path in entries - Passwords will no longer be copied to the clipboard by default diff --git a/app/src/androidTest/java/com/zeapo/pwdstore/MigrationsTest.kt b/app/src/androidTest/java/com/zeapo/pwdstore/MigrationsTest.kt new file mode 100644 index 00000000..d1b04fc3 --- /dev/null +++ b/app/src/androidTest/java/com/zeapo/pwdstore/MigrationsTest.kt @@ -0,0 +1,81 @@ +/* + * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved. + * SPDX-License-Identifier: GPL-3.0-only + */ + +@file:Suppress("DEPRECATION") +package com.zeapo.pwdstore + +import android.content.Context +import androidx.core.content.edit +import com.zeapo.pwdstore.utils.PreferenceKeys +import com.zeapo.pwdstore.utils.getString +import com.zeapo.pwdstore.utils.sharedPrefs +import org.junit.Test + +import org.junit.Assert.* + +class MigrationsTest { + + private fun checkOldKeysAreRemoved(context: Context) = with(context.sharedPrefs) { + assertNull(getString(PreferenceKeys.GIT_REMOTE_PORT)) + assertNull(getString(PreferenceKeys.GIT_REMOTE_USERNAME)) + assertNull(getString(PreferenceKeys.GIT_REMOTE_SERVER)) + assertNull(getString(PreferenceKeys.GIT_REMOTE_LOCATION)) + } + + @Test + fun verifySshWithCustomPortMigration() { + val context = Application.instance.applicationContext + context.sharedPrefs.edit { + clear() + putString(PreferenceKeys.GIT_REMOTE_PORT, "2200") + putString(PreferenceKeys.GIT_REMOTE_USERNAME, "msfjarvis") + putString(PreferenceKeys.GIT_REMOTE_LOCATION, "/mnt/disk3/pass-repo") + putString(PreferenceKeys.GIT_REMOTE_SERVER, "192.168.0.102") + putString(PreferenceKeys.GIT_REMOTE_PROTOCOL, "ssh://") + } + runMigrations(context) + checkOldKeysAreRemoved(context) + assertEquals( + context.sharedPrefs.getString(PreferenceKeys.GIT_REMOTE_URL), + "ssh://msfjarvis@192.168.0.102:2200/mnt/disk3/pass-repo" + ) + } + + @Test + fun verifySshWithDefaultPortMigration() { + val context = Application.instance.applicationContext + context.sharedPrefs.edit { + clear() + putString(PreferenceKeys.GIT_REMOTE_USERNAME, "msfjarvis") + putString(PreferenceKeys.GIT_REMOTE_LOCATION, "/mnt/disk3/pass-repo") + putString(PreferenceKeys.GIT_REMOTE_SERVER, "192.168.0.102") + putString(PreferenceKeys.GIT_REMOTE_PROTOCOL, "ssh://") + } + runMigrations(context) + checkOldKeysAreRemoved(context) + assertEquals( + context.sharedPrefs.getString(PreferenceKeys.GIT_REMOTE_URL), + "msfjarvis@192.168.0.102:/mnt/disk3/pass-repo" + ) + } + + @Test + fun verifyHttpsWithGitHubMigration() { + val context = Application.instance.applicationContext + context.sharedPrefs.edit { + clear() + putString(PreferenceKeys.GIT_REMOTE_USERNAME, "msfjarvis") + putString(PreferenceKeys.GIT_REMOTE_LOCATION, "Android-Password-Store/pass-test") + putString(PreferenceKeys.GIT_REMOTE_SERVER, "github.com") + putString(PreferenceKeys.GIT_REMOTE_PROTOCOL, "https://") + } + runMigrations(context) + checkOldKeysAreRemoved(context) + assertEquals( + context.sharedPrefs.getString(PreferenceKeys.GIT_REMOTE_URL), + "https://github.com/Android-Password-Store/pass-test" + ) + } +} diff --git a/app/src/androidTest/java/com/zeapo/pwdstore/git/GitServerConfigActivityTest.kt b/app/src/androidTest/java/com/zeapo/pwdstore/git/GitServerConfigActivityTest.kt deleted file mode 100644 index 2c2691b1..00000000 --- a/app/src/androidTest/java/com/zeapo/pwdstore/git/GitServerConfigActivityTest.kt +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -package com.zeapo.pwdstore.git - -import android.view.View -import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.UiController -import androidx.test.espresso.ViewAction -import androidx.test.espresso.action.ViewActions.replaceText -import androidx.test.espresso.matcher.ViewMatchers.isEnabled -import androidx.test.espresso.matcher.ViewMatchers.withId -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.rule.ActivityTestRule -import com.google.android.material.button.MaterialButtonToggleGroup -import com.zeapo.pwdstore.R -import com.zeapo.pwdstore.git.BaseGitActivity.GitUpdateUrlResult -import kotlin.test.assertEquals -import org.hamcrest.Matcher -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -class GitServerConfigActivityTest { - - @Rule - @JvmField - val activityRule = ActivityTestRule(GitServerConfigActivity::class.java, true) - - private val activity get() = activityRule.activity - - @Test - fun invalidValuesFailPredictably() { - setDefaultSshValues() - onView(withId(R.id.server_port)).perform(replaceText("69420")) - assertEquals(activity.updateUrl(), GitUpdateUrlResult.CustomPortRequiresAbsoluteUrlError) - - setDefaultSshValues() - onView(withId(R.id.server_url)).perform(replaceText("")) - assertEquals(activity.updateUrl(), GitUpdateUrlResult.EmptyHostnameError) - - setDefaultSshValues() - onView(withId(R.id.server_port)).perform(replaceText("xyz_is_not_a_port")) - assertEquals(activity.updateUrl(), GitUpdateUrlResult.NonNumericPortError) - - setDefaultHttpsValues() - onView(withId(R.id.server_port)).perform(replaceText("xyz_is_not_a_port")) - assertEquals(activity.updateUrl(), GitUpdateUrlResult.NonNumericPortError) - - setDefaultHttpsValues() - onView(withId(R.id.server_url)).perform(replaceText("")) - assertEquals(activity.updateUrl(), GitUpdateUrlResult.EmptyHostnameError) - } - - @Test - fun urlIsConstructedCorrectly() { - setDefaultSshValues() - activity.updateUrl() - assertEquals("john_doe@github.com:john_doe/my_secret_repository", activity.url) - - setDefaultSshValues() - onView(withId(R.id.server_port)).perform(replaceText("69420")) - onView(withId(R.id.server_url)).perform(replaceText("192.168.0.102")) - onView(withId(R.id.server_path)).perform(replaceText("/home/john_doe/my_secret_repository")) - activity.updateUrl() - assertEquals("ssh://john_doe@192.168.0.102:69420/home/john_doe/my_secret_repository", activity.url) - - setDefaultHttpsValues() - activity.updateUrl() - assertEquals("https://github.com/john_doe/my_secret_repository", activity.url) - - setDefaultHttpsValues() - onView(withId(R.id.server_port)).perform(replaceText("69420")) - onView(withId(R.id.server_url)).perform(replaceText("192.168.0.102")) - onView(withId(R.id.server_path)).perform(replaceText("/home/john_doe/my_secret_repository")) - activity.updateUrl() - assertEquals("https://192.168.0.102:69420/home/john_doe/my_secret_repository", activity.url) - } - - private fun callMethod(message: String = "", viewMethod: (view: T) -> Unit): ViewAction { - return object : ViewAction { - override fun getDescription(): String { - return if (message.isBlank()) viewMethod.toString() else message - } - - override fun getConstraints(): Matcher { - return isEnabled() - } - - @Suppress("UNCHECKED_CAST") - override fun perform(uiController: UiController?, view: View?) { - viewMethod(view!! as T) - } - - } - } - - private fun setDefaultHttpsValues() { - onView(withId(R.id.clone_protocol_group)).perform(callMethod { - it.check(R.id.clone_protocol_https) - }) - onView(withId(R.id.connection_mode_group)).perform(callMethod { - it.check(R.id.connection_mode_password) - }) - onView(withId(R.id.server_path)).perform(replaceText("john_doe/my_secret_repository")) - onView(withId(R.id.server_port)).perform(replaceText("")) - onView(withId(R.id.server_url)).perform(replaceText("github.com")) - onView(withId(R.id.server_user)).perform(replaceText("john_doe")) - } - - private fun setDefaultSshValues() { - onView(withId(R.id.clone_protocol_group)).perform(callMethod { - it.check(R.id.clone_protocol_ssh) - }) - onView(withId(R.id.connection_mode_group)).perform(callMethod { - it.check(R.id.connection_mode_ssh_key) - }) - onView(withId(R.id.server_path)).perform(replaceText("john_doe/my_secret_repository")) - onView(withId(R.id.server_port)).perform(replaceText("")) - onView(withId(R.id.server_url)).perform(replaceText("github.com")) - onView(withId(R.id.server_user)).perform(replaceText("john_doe")) - } -} diff --git a/app/src/main/java/com/zeapo/pwdstore/Application.kt b/app/src/main/java/com/zeapo/pwdstore/Application.kt index c3b9a90d..544eb047 100644 --- a/app/src/main/java/com/zeapo/pwdstore/Application.kt +++ b/app/src/main/java/com/zeapo/pwdstore/Application.kt @@ -10,12 +10,12 @@ import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES -import androidx.preference.PreferenceManager import com.github.ajalt.timberkt.Timber.DebugTree import com.github.ajalt.timberkt.Timber.plant import com.zeapo.pwdstore.git.config.setUpBouncyCastleForSshj import com.zeapo.pwdstore.utils.PreferenceKeys import com.zeapo.pwdstore.utils.sharedPrefs +import com.zeapo.pwdstore.utils.getString @Suppress("Unused") class Application : android.app.Application(), SharedPreferences.OnSharedPreferenceChangeListener { @@ -30,6 +30,7 @@ class Application : android.app.Application(), SharedPreferences.OnSharedPrefere sharedPrefs.registerOnSharedPreferenceChangeListener(this) setNightMode() setUpBouncyCastleForSshj() + runMigrations(applicationContext) } override fun onTerminate() { @@ -44,7 +45,7 @@ class Application : android.app.Application(), SharedPreferences.OnSharedPrefere } private fun setNightMode() { - AppCompatDelegate.setDefaultNightMode(when (sharedPrefs.getString(PreferenceKeys.APP_THEME, getString(R.string.app_theme_def))) { + AppCompatDelegate.setDefaultNightMode(when (sharedPrefs.getString(PreferenceKeys.APP_THEME) ?: getString(R.string.app_theme_def)) { "light" -> MODE_NIGHT_NO "dark" -> MODE_NIGHT_YES "follow_system" -> MODE_NIGHT_FOLLOW_SYSTEM diff --git a/app/src/main/java/com/zeapo/pwdstore/ClipboardService.kt b/app/src/main/java/com/zeapo/pwdstore/ClipboardService.kt index 20a36539..d02a333a 100644 --- a/app/src/main/java/com/zeapo/pwdstore/ClipboardService.kt +++ b/app/src/main/java/com/zeapo/pwdstore/ClipboardService.kt @@ -15,10 +15,10 @@ import android.os.Build import android.os.IBinder import androidx.core.app.NotificationCompat import androidx.core.content.getSystemService -import androidx.preference.PreferenceManager import com.github.ajalt.timberkt.d import com.zeapo.pwdstore.utils.PreferenceKeys import com.zeapo.pwdstore.utils.clipboard +import com.zeapo.pwdstore.utils.getString import com.zeapo.pwdstore.utils.sharedPrefs import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -46,7 +46,7 @@ class ClipboardService : Service() { ACTION_START -> { val time = try { - Integer.parseInt(settings.getString(PreferenceKeys.GENERAL_SHOW_TIME, "45") as String) + Integer.parseInt(settings.getString(PreferenceKeys.GENERAL_SHOW_TIME) ?: "45") } catch (e: NumberFormatException) { 45 } diff --git a/app/src/main/java/com/zeapo/pwdstore/LaunchActivity.kt b/app/src/main/java/com/zeapo/pwdstore/LaunchActivity.kt index 71f59851..430f683a 100644 --- a/app/src/main/java/com/zeapo/pwdstore/LaunchActivity.kt +++ b/app/src/main/java/com/zeapo/pwdstore/LaunchActivity.kt @@ -9,7 +9,6 @@ import android.os.Bundle import android.os.Handler import androidx.appcompat.app.AppCompatActivity import androidx.core.content.edit -import androidx.preference.PreferenceManager import com.zeapo.pwdstore.crypto.DecryptActivity import com.zeapo.pwdstore.utils.BiometricAuthenticator import com.zeapo.pwdstore.utils.PreferenceKeys diff --git a/app/src/main/java/com/zeapo/pwdstore/Migrations.kt b/app/src/main/java/com/zeapo/pwdstore/Migrations.kt new file mode 100644 index 00000000..1e1943d8 --- /dev/null +++ b/app/src/main/java/com/zeapo/pwdstore/Migrations.kt @@ -0,0 +1,83 @@ +/* + * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved. + * SPDX-License-Identifier: GPL-3.0-only + */ +@file:Suppress("DEPRECATION") + +package com.zeapo.pwdstore + +import android.content.Context +import androidx.core.content.edit +import com.github.ajalt.timberkt.e +import com.github.ajalt.timberkt.i +import com.zeapo.pwdstore.git.config.GitSettings +import com.zeapo.pwdstore.git.config.Protocol +import com.zeapo.pwdstore.utils.PreferenceKeys +import com.zeapo.pwdstore.utils.getString +import com.zeapo.pwdstore.utils.sharedPrefs +import java.net.URI + +fun runMigrations(context: Context) { + migrateToGitUrlBasedConfig(context) +} + +private fun migrateToGitUrlBasedConfig(context: Context) { + val serverHostname = context.sharedPrefs.getString(PreferenceKeys.GIT_REMOTE_SERVER) + ?: return + i { "Migrating to URL-based Git config" } + val serverPort = context.sharedPrefs.getString(PreferenceKeys.GIT_REMOTE_PORT) ?: "" + val serverUser = context.sharedPrefs.getString(PreferenceKeys.GIT_REMOTE_USERNAME) ?: "" + val serverPath = context.sharedPrefs.getString(PreferenceKeys.GIT_REMOTE_LOCATION) ?: "" + val protocol = Protocol.fromString(context.sharedPrefs.getString(PreferenceKeys.GIT_REMOTE_PROTOCOL)) + + // Whether we need the leading ssh:// depends on the use of a custom port. + val hostnamePart = serverHostname.removePrefix("ssh://") + val url = when (protocol) { + Protocol.Ssh -> { + val userPart = if (serverUser.isEmpty()) "" else "${serverUser.trimEnd('@')}@" + val portPart = + if (serverPort == "22" || serverPort.isEmpty()) "" else ":$serverPort" + if (portPart.isEmpty()) { + "$userPart$hostnamePart:$serverPath" + } else { + // Only absolute paths are supported with custom ports. + if (!serverPath.startsWith('/')) + null + else + // We have to specify the ssh scheme as this is the only way to pass a custom + // port. + "ssh://$userPart$hostnamePart$portPart$serverPath" + } + } + Protocol.Https -> { + val portPart = + if (serverPort == "443" || serverPort.isEmpty()) "" else ":$serverPort" + val pathPart = serverPath.trimStart('/', ':') + val urlWithFreeEntryScheme = "$hostnamePart$portPart/$pathPart" + val url = when { + urlWithFreeEntryScheme.startsWith("https://") -> urlWithFreeEntryScheme + urlWithFreeEntryScheme.startsWith("http://") -> urlWithFreeEntryScheme.replaceFirst("http", "https") + else -> "https://$urlWithFreeEntryScheme" + } + try { + if (URI(url).rawAuthority != null) + url + else + null + } catch (_: Exception) { + null + } + } + } + + context.sharedPrefs.edit { + remove(PreferenceKeys.GIT_REMOTE_LOCATION) + remove(PreferenceKeys.GIT_REMOTE_PORT) + remove(PreferenceKeys.GIT_REMOTE_SERVER) + remove(PreferenceKeys.GIT_REMOTE_USERNAME) + } + if (url == null || !GitSettings.updateUrlIfValid(url)) { + e { "Failed to migrate to URL-based Git config, generated URL is invalid" } + } +} + diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt index c9d00353..6657afaf 100644 --- a/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt +++ b/app/src/main/java/com/zeapo/pwdstore/PasswordFragment.kt @@ -26,12 +26,12 @@ import com.zeapo.pwdstore.git.BaseGitActivity import com.zeapo.pwdstore.git.GitOperationActivity import com.zeapo.pwdstore.git.GitServerConfigActivity import com.zeapo.pwdstore.git.config.ConnectionMode +import com.zeapo.pwdstore.git.config.GitSettings import com.zeapo.pwdstore.ui.OnOffItemAnimator import com.zeapo.pwdstore.ui.adapters.PasswordItemRecyclerAdapter import com.zeapo.pwdstore.ui.dialogs.ItemCreationBottomSheet import com.zeapo.pwdstore.utils.PasswordItem import com.zeapo.pwdstore.utils.PasswordRepository -import com.zeapo.pwdstore.utils.PreferenceKeys import com.zeapo.pwdstore.utils.sharedPrefs import com.zeapo.pwdstore.utils.viewBinding import java.io.File @@ -91,8 +91,7 @@ class PasswordFragment : Fragment(R.layout.password_recycler_view) { } else { // When authentication is set to ConnectionMode.None then the only git operation we // can run is a pull, so automatically fallback to that. - val operationId = when (ConnectionMode.fromString(settings.getString - (PreferenceKeys.GIT_REMOTE_AUTH, null))) { + val operationId = when (GitSettings.connectionMode) { ConnectionMode.None -> BaseGitActivity.REQUEST_PULL else -> BaseGitActivity.REQUEST_SYNC } diff --git a/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt b/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt index 3f31a2c5..8223b127 100644 --- a/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt +++ b/app/src/main/java/com/zeapo/pwdstore/PasswordStore.kt @@ -7,7 +7,6 @@ package com.zeapo.pwdstore import android.Manifest import android.annotation.SuppressLint import android.content.Intent -import android.content.SharedPreferences import android.content.pm.PackageManager import android.content.pm.ShortcutInfo.Builder import android.content.pm.ShortcutManager @@ -34,7 +33,6 @@ import androidx.fragment.app.FragmentManager import androidx.fragment.app.commit import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope -import androidx.preference.PreferenceManager import com.github.ajalt.timberkt.d import com.github.ajalt.timberkt.e import com.github.ajalt.timberkt.i @@ -52,6 +50,7 @@ import com.zeapo.pwdstore.git.BaseGitActivity import com.zeapo.pwdstore.git.GitOperationActivity import com.zeapo.pwdstore.git.GitServerConfigActivity import com.zeapo.pwdstore.git.config.ConnectionMode +import com.zeapo.pwdstore.git.config.GitSettings import com.zeapo.pwdstore.ui.dialogs.FolderCreationDialogFragment import com.zeapo.pwdstore.utils.PasswordItem import com.zeapo.pwdstore.utils.PasswordRepository @@ -66,6 +65,7 @@ import com.zeapo.pwdstore.utils.PasswordRepository.PasswordSortOrder.Companion.g import com.zeapo.pwdstore.utils.PreferenceKeys import com.zeapo.pwdstore.utils.commitChange import com.zeapo.pwdstore.utils.contains +import com.zeapo.pwdstore.utils.getString import com.zeapo.pwdstore.utils.isInsideRepository import com.zeapo.pwdstore.utils.listFilesRecursively import com.zeapo.pwdstore.utils.requestInputFocusOnView @@ -114,8 +114,8 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) { private val directoryChangeAction = registerForActivityResult(StartActivityForResult()) { result -> if (result.resultCode == RESULT_OK) { if (settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false) && - settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO, null) != null) { - val externalRepoPath = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO, null) + settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO) != null) { + val externalRepoPath = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO) val dir = externalRepoPath?.let { File(it) } if (dir != null && dir.exists() && @@ -252,8 +252,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) { override fun onCreateOptionsMenu(menu: Menu?): Boolean { val menuRes = when { - ConnectionMode.fromString(settings.getString(PreferenceKeys.GIT_REMOTE_AUTH, null)) - == ConnectionMode.None -> R.menu.main_menu_no_auth + GitSettings.connectionMode == ConnectionMode.None -> R.menu.main_menu_no_auth PasswordRepository.isGitRepo() -> R.menu.main_menu_git else -> R.menu.main_menu_non_git } @@ -403,7 +402,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) { private fun initializeRepositoryInfo() { val externalRepo = settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false) - val externalRepoPath = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO, null) + val externalRepoPath = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO) if (externalRepo && !hasRequiredStoragePermissions()) { return } @@ -849,7 +848,7 @@ class PasswordStore : AppCompatActivity(R.layout.activity_pwdstore) { } .setNegativeButton(resources.getString(R.string.location_sdcard)) { _, _ -> settings.edit { putBoolean(PreferenceKeys.GIT_EXTERNAL, true) } - val externalRepo = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO, null) + val externalRepo = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO) if (externalRepo == null) { val intent = Intent(activity, UserPreference::class.java) intent.putExtra("operation", "git_external") diff --git a/app/src/main/java/com/zeapo/pwdstore/SearchableRepositoryViewModel.kt b/app/src/main/java/com/zeapo/pwdstore/SearchableRepositoryViewModel.kt index f1728d6d..7ffa71a3 100644 --- a/app/src/main/java/com/zeapo/pwdstore/SearchableRepositoryViewModel.kt +++ b/app/src/main/java/com/zeapo/pwdstore/SearchableRepositoryViewModel.kt @@ -15,7 +15,6 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.asFlow import androidx.lifecycle.asLiveData -import androidx.preference.PreferenceManager import androidx.recyclerview.selection.ItemDetailsLookup import androidx.recyclerview.selection.ItemKeyProvider import androidx.recyclerview.selection.Selection diff --git a/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt b/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt index 43cab0a0..7f01e063 100644 --- a/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt +++ b/app/src/main/java/com/zeapo/pwdstore/UserPreference.kt @@ -34,7 +34,6 @@ import androidx.preference.EditTextPreference import androidx.preference.ListPreference import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat -import androidx.preference.PreferenceManager import androidx.preference.SwitchPreferenceCompat import com.github.ajalt.timberkt.Timber.tag import com.github.ajalt.timberkt.d @@ -54,6 +53,7 @@ import com.zeapo.pwdstore.utils.PasswordRepository import com.zeapo.pwdstore.utils.PreferenceKeys import com.zeapo.pwdstore.utils.autofillManager import com.zeapo.pwdstore.utils.getEncryptedPrefs +import com.zeapo.pwdstore.utils.getString import com.zeapo.pwdstore.utils.sharedPrefs import java.io.File import java.io.IOException @@ -141,11 +141,11 @@ class UserPreference : AppCompatActivity() { // Misc preferences val appVersionPreference = findPreference(PreferenceKeys.APP_VERSION) - selectExternalGitRepositoryPreference?.summary = sharedPreferences.getString(PreferenceKeys.GIT_EXTERNAL_REPO, getString(R.string.no_repo_selected)) + selectExternalGitRepositoryPreference?.summary = sharedPreferences.getString(PreferenceKeys.GIT_EXTERNAL_REPO) ?: getString(R.string.no_repo_selected) viewSshKeyPreference?.isVisible = sharedPreferences.getBoolean(PreferenceKeys.USE_GENERATED_KEY, false) deleteRepoPreference?.isVisible = !sharedPreferences.getBoolean(PreferenceKeys.GIT_EXTERNAL, false) - clearClipboard20xPreference?.isVisible = sharedPreferences.getString(PreferenceKeys.GENERAL_SHOW_TIME, "45")?.toInt() != 0 - openkeystoreIdPreference?.isVisible = sharedPreferences.getString(PreferenceKeys.SSH_OPENKEYSTORE_KEYID, null)?.isNotEmpty() + clearClipboard20xPreference?.isVisible = sharedPreferences.getString(PreferenceKeys.GENERAL_SHOW_TIME)?.toInt() != 0 + openkeystoreIdPreference?.isVisible = sharedPreferences.getString(PreferenceKeys.SSH_OPENKEYSTORE_KEYID)?.isNotEmpty() ?: false updateAutofillSettings() @@ -171,9 +171,9 @@ class UserPreference : AppCompatActivity() { clearSavedPassPreference?.onPreferenceClickListener = ClickListener { encryptedPreferences.edit { - if (encryptedPreferences.getString(PreferenceKeys.HTTPS_PASSWORD, null) != null) + if (encryptedPreferences.getString(PreferenceKeys.HTTPS_PASSWORD) != null) remove(PreferenceKeys.HTTPS_PASSWORD) - else if (encryptedPreferences.getString(PreferenceKeys.SSH_KEY_LOCAL_PASSPHRASE, null) != null) + else if (encryptedPreferences.getString(PreferenceKeys.SSH_KEY_LOCAL_PASSPHRASE) != null) remove(PreferenceKeys.SSH_KEY_LOCAL_PASSPHRASE) } updateClearSavedPassphrasePrefs() @@ -226,7 +226,7 @@ class UserPreference : AppCompatActivity() { } selectExternalGitRepositoryPreference?.summary = - sharedPreferences.getString(PreferenceKeys.GIT_EXTERNAL_REPO, context.getString(R.string.no_repo_selected)) + sharedPreferences.getString(PreferenceKeys.GIT_EXTERNAL_REPO) ?: context.getString(R.string.no_repo_selected) selectExternalGitRepositoryPreference?.onPreferenceClickListener = ClickListener { prefsActivity.selectExternalGitRepository() true @@ -321,7 +321,7 @@ class UserPreference : AppCompatActivity() { prefsActivity.storeCustomDictionaryPath() true } - val dictUri = sharedPreferences.getString(PreferenceKeys.PREF_KEY_CUSTOM_DICT, "") + val dictUri = sharedPreferences.getString(PreferenceKeys.PREF_KEY_CUSTOM_DICT) ?: "" if (!TextUtils.isEmpty(dictUri)) { setCustomDictSummary(prefCustomXkpwdDictionary, Uri.parse(dictUri)) @@ -377,8 +377,8 @@ class UserPreference : AppCompatActivity() { private fun updateClearSavedPassphrasePrefs() { clearSavedPassPreference?.apply { - val sshPass = encryptedPreferences.getString(PreferenceKeys.SSH_KEY_LOCAL_PASSPHRASE, null) - val httpsPass = encryptedPreferences.getString(PreferenceKeys.HTTPS_PASSWORD, null) + val sshPass = encryptedPreferences.getString(PreferenceKeys.SSH_KEY_LOCAL_PASSPHRASE) + val httpsPass = encryptedPreferences.getString(PreferenceKeys.HTTPS_PASSWORD) if (sshPass == null && httpsPass == null) { isVisible = false return@apply diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillHelper.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillHelper.kt index d5c7af75..14bcb501 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillHelper.kt +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillHelper.kt @@ -23,6 +23,8 @@ import com.zeapo.pwdstore.R import com.zeapo.pwdstore.model.PasswordEntry import com.zeapo.pwdstore.utils.PasswordRepository import com.zeapo.pwdstore.utils.PreferenceKeys +import com.zeapo.pwdstore.utils.getString +import com.zeapo.pwdstore.utils.sharedPrefs import java.io.File import java.security.MessageDigest @@ -37,11 +39,7 @@ private fun ByteArray.base64(): String { return Base64.encodeToString(this, Base64.NO_WRAP) } -private fun Context.getDefaultUsername(): String? { - return PreferenceManager - .getDefaultSharedPreferences(this) - .getString(PreferenceKeys.OREO_AUTOFILL_DEFAULT_USERNAME, null) -} +private fun Context.getDefaultUsername() = sharedPrefs.getString(PreferenceKeys.OREO_AUTOFILL_DEFAULT_USERNAME) private fun stableHash(array: Collection): String { val hashes = array.map { it.sha256().base64() } diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillPreferences.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillPreferences.kt index 3e10933a..9c84e1ad 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillPreferences.kt +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/AutofillPreferences.kt @@ -5,10 +5,8 @@ package com.zeapo.pwdstore.autofill.oreo import android.content.Context -import android.content.SharedPreferences import android.os.Build import androidx.annotation.RequiresApi -import androidx.preference.PreferenceManager import com.zeapo.pwdstore.utils.sharedPrefs import java.io.File import java.nio.file.Paths diff --git a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/PublicSuffixListCache.kt b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/PublicSuffixListCache.kt index 536357f5..8107248e 100644 --- a/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/PublicSuffixListCache.kt +++ b/app/src/main/java/com/zeapo/pwdstore/autofill/oreo/PublicSuffixListCache.kt @@ -6,8 +6,8 @@ package com.zeapo.pwdstore.autofill.oreo import android.content.Context import android.util.Patterns -import androidx.preference.PreferenceManager import com.zeapo.pwdstore.utils.PreferenceKeys +import com.zeapo.pwdstore.utils.getString import com.zeapo.pwdstore.utils.sharedPrefs import kotlinx.coroutines.runBlocking import mozilla.components.lib.publicsuffixlist.PublicSuffixList @@ -69,9 +69,10 @@ fun getSuffixPlusUpToOne(domain: String, suffix: String): String? { } fun getCustomSuffixes(context: Context): Sequence { - return context.sharedPrefs.getString(PreferenceKeys.OREO_AUTOFILL_CUSTOM_PUBLIC_SUFFIXES, "")!! - .splitToSequence('\n') - .filter { it.isNotBlank() && it.first() != '.' && it.last() != '.' } + return context.sharedPrefs.getString(PreferenceKeys.OREO_AUTOFILL_CUSTOM_PUBLIC_SUFFIXES) + ?.splitToSequence('\n') + ?.filter { it.isNotBlank() && it.first() != '.' && it.last() != '.' } + ?: emptySequence() } suspend fun getCanonicalSuffix(context: Context, domain: String): String { diff --git a/app/src/main/java/com/zeapo/pwdstore/crypto/BasePgpActivity.kt b/app/src/main/java/com/zeapo/pwdstore/crypto/BasePgpActivity.kt index fd7a8794..06f03ed9 100644 --- a/app/src/main/java/com/zeapo/pwdstore/crypto/BasePgpActivity.kt +++ b/app/src/main/java/com/zeapo/pwdstore/crypto/BasePgpActivity.kt @@ -30,6 +30,7 @@ import com.zeapo.pwdstore.R import com.zeapo.pwdstore.utils.OPENPGP_PROVIDER import com.zeapo.pwdstore.utils.PreferenceKeys import com.zeapo.pwdstore.utils.clipboard +import com.zeapo.pwdstore.utils.getString import com.zeapo.pwdstore.utils.sharedPrefs import com.zeapo.pwdstore.utils.snackbar import java.io.File @@ -254,8 +255,7 @@ open class BasePgpActivity : AppCompatActivity(), OpenPgpServiceConnection.OnBou var clearAfter = 45 try { - clearAfter = (settings.getString(PreferenceKeys.GENERAL_SHOW_TIME, "45") - ?: "45").toInt() + clearAfter = (settings.getString(PreferenceKeys.GENERAL_SHOW_TIME) ?: "45").toInt() } catch (_: NumberFormatException) { } diff --git a/app/src/main/java/com/zeapo/pwdstore/crypto/PasswordCreationActivity.kt b/app/src/main/java/com/zeapo/pwdstore/crypto/PasswordCreationActivity.kt index 177e1499..e42bfef3 100644 --- a/app/src/main/java/com/zeapo/pwdstore/crypto/PasswordCreationActivity.kt +++ b/app/src/main/java/com/zeapo/pwdstore/crypto/PasswordCreationActivity.kt @@ -31,6 +31,7 @@ import com.zeapo.pwdstore.ui.dialogs.XkPasswordGeneratorDialogFragment import com.zeapo.pwdstore.utils.PasswordRepository import com.zeapo.pwdstore.utils.PreferenceKeys import com.zeapo.pwdstore.utils.commitChange +import com.zeapo.pwdstore.utils.getString import com.zeapo.pwdstore.utils.isInsideRepository import com.zeapo.pwdstore.utils.snackbar import com.zeapo.pwdstore.utils.viewBinding @@ -220,7 +221,7 @@ class PasswordCreationActivity : BasePgpActivity(), OpenPgpServiceConnection.OnB } private fun generatePassword() { - when (settings.getString(PreferenceKeys.PREF_KEY_PWGEN_TYPE, KEY_PWGEN_TYPE_CLASSIC)) { + when (settings.getString(PreferenceKeys.PREF_KEY_PWGEN_TYPE) ?: KEY_PWGEN_TYPE_CLASSIC) { KEY_PWGEN_TYPE_CLASSIC -> PasswordGeneratorDialogFragment() .show(supportFragmentManager, "generator") KEY_PWGEN_TYPE_XKPASSWD -> XkPasswordGeneratorDialogFragment() diff --git a/app/src/main/java/com/zeapo/pwdstore/git/BaseGitActivity.kt b/app/src/main/java/com/zeapo/pwdstore/git/BaseGitActivity.kt index 06f85d1b..fb0831d6 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/BaseGitActivity.kt +++ b/app/src/main/java/com/zeapo/pwdstore/git/BaseGitActivity.kt @@ -5,20 +5,15 @@ package com.zeapo.pwdstore.git import android.content.Intent -import android.content.SharedPreferences -import android.os.Bundle import android.view.MenuItem import androidx.annotation.CallSuper import androidx.appcompat.app.AppCompatActivity -import androidx.core.content.edit -import androidx.core.text.isDigitsOnly import androidx.lifecycle.lifecycleScope import com.github.ajalt.timberkt.Timber.tag import com.github.ajalt.timberkt.e import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.zeapo.pwdstore.R import com.zeapo.pwdstore.git.config.ConnectionMode -import com.zeapo.pwdstore.git.config.Protocol +import com.zeapo.pwdstore.git.config.GitSettings import com.zeapo.pwdstore.git.config.SshApiSessionFactory import com.zeapo.pwdstore.git.operation.BreakOutOfDetached import com.zeapo.pwdstore.git.operation.CloneOperation @@ -28,11 +23,6 @@ import com.zeapo.pwdstore.git.operation.PushOperation import com.zeapo.pwdstore.git.operation.ResetToRemoteOperation import com.zeapo.pwdstore.git.operation.SyncOperation import com.zeapo.pwdstore.utils.PasswordRepository -import com.zeapo.pwdstore.utils.PreferenceKeys -import com.zeapo.pwdstore.utils.getEncryptedPrefs -import com.zeapo.pwdstore.utils.sharedPrefs -import java.io.File -import java.net.URI import kotlinx.coroutines.launch /** @@ -41,39 +31,8 @@ import kotlinx.coroutines.launch */ abstract class BaseGitActivity : AppCompatActivity() { - lateinit var protocol: Protocol - lateinit var connectionMode: ConnectionMode - var url: String? = null - lateinit var serverHostname: String - lateinit var serverPort: String - lateinit var serverUser: String - lateinit var serverPath: String - lateinit var username: String - lateinit var email: String - lateinit var branch: String private var identityBuilder: SshApiSessionFactory.IdentityBuilder? = null private var identity: SshApiSessionFactory.ApiIdentity? = null - lateinit var settings: SharedPreferences - private set - private lateinit var encryptedSettings: SharedPreferences - - @CallSuper - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - settings = sharedPrefs - encryptedSettings = getEncryptedPrefs("git_operation") - protocol = Protocol.fromString(settings.getString(PreferenceKeys.GIT_REMOTE_PROTOCOL, null)) - connectionMode = ConnectionMode.fromString(settings.getString(PreferenceKeys.GIT_REMOTE_AUTH, null)) - serverHostname = settings.getString(PreferenceKeys.GIT_REMOTE_SERVER, null) ?: "" - serverPort = settings.getString(PreferenceKeys.GIT_REMOTE_PORT, null) ?: "" - serverUser = settings.getString(PreferenceKeys.GIT_REMOTE_USERNAME, null) ?: "" - serverPath = settings.getString(PreferenceKeys.GIT_REMOTE_LOCATION, null) ?: "" - username = settings.getString(PreferenceKeys.GIT_CONFIG_USER_NAME, null) ?: "" - email = settings.getString(PreferenceKeys.GIT_CONFIG_USER_EMAIL, null) ?: "" - branch = settings.getString(PreferenceKeys.GIT_BRANCH_NAME, null) ?: "master" - updateUrl() - } override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { @@ -95,77 +54,6 @@ abstract class BaseGitActivity : AppCompatActivity() { super.onDestroy() } - enum class GitUpdateUrlResult(val textRes: Int) { - Ok(0), - CustomPortRequiresAbsoluteUrlError(R.string.git_config_error_custom_port_absolute), - EmptyHostnameError(R.string.git_config_error_hostname_empty), - GenericError(R.string.git_config_error_generic), - NonNumericPortError(R.string.git_config_error_nonnumeric_port) - } - - /** - * Update the [url] field with the values that build it up. This function returns a - * [GitUpdateUrlResult] indicating whether the values could be used to build a URL and only adds - * the `origin` remote when they were. This check is not perfect, it is mostly meant to catch - * syntax-related typos. - */ - fun updateUrl(): GitUpdateUrlResult { - if (serverHostname.isEmpty()) - return GitUpdateUrlResult.EmptyHostnameError - if (!serverPort.isDigitsOnly()) - return GitUpdateUrlResult.NonNumericPortError - - val previousUrl = url ?: "" - // Whether we need the leading ssh:// depends on the use of a custom port. - val hostnamePart = serverHostname.removePrefix("ssh://") - val newUrl = when (protocol) { - Protocol.Ssh -> { - val userPart = if (serverUser.isEmpty()) "" else "${serverUser.trimEnd('@')}@" - val portPart = - if (serverPort == "22" || serverPort.isEmpty()) "" else ":$serverPort" - if (portPart.isEmpty()) { - "$userPart$hostnamePart:$serverPath" - } else { - // Only absolute paths are supported with custom ports. - if (!serverPath.startsWith('/')) - return GitUpdateUrlResult.CustomPortRequiresAbsoluteUrlError - val pathPart = serverPath - // We have to specify the ssh scheme as this is the only way to pass a custom - // port. - "ssh://$userPart$hostnamePart$portPart$pathPart" - } - } - Protocol.Https -> { - val portPart = - if (serverPort == "443" || serverPort.isEmpty()) "" else ":$serverPort" - val pathPart = serverPath.trimStart('/', ':') - val urlWithFreeEntryScheme = "$hostnamePart$portPart/$pathPart" - val url = when { - urlWithFreeEntryScheme.startsWith("https://") -> urlWithFreeEntryScheme - urlWithFreeEntryScheme.startsWith("http://") -> urlWithFreeEntryScheme.replaceFirst("http", "https") - else -> "https://$urlWithFreeEntryScheme" - } - try { - if (URI(url).rawAuthority != null) - url - else - return GitUpdateUrlResult.GenericError - } catch (_: Exception) { - return GitUpdateUrlResult.GenericError - } - } - } - if (PasswordRepository.isInitialized) - PasswordRepository.addRemote("origin", newUrl, true) - // When the server changes, remote password and host key file should be deleted. - if (previousUrl.isNotEmpty() && newUrl != previousUrl) { - encryptedSettings.edit { remove(PreferenceKeys.HTTPS_PASSWORD) } - File("$filesDir/.host_key").delete() - } - url = newUrl - return GitUpdateUrlResult.Ok - } - /** * Attempt to launch the requested Git operation. Depending on the configured auth, it may not * be possible to launch the operation immediately. In that case, this function may launch an @@ -176,7 +64,7 @@ abstract class BaseGitActivity : AppCompatActivity() { * @param operation The type of git operation to launch */ suspend fun launchGitOperation(operation: Int) { - if (url == null) { + if (GitSettings.url == null) { setResult(RESULT_CANCELED) finish() return @@ -185,7 +73,7 @@ abstract class BaseGitActivity : AppCompatActivity() { // Before launching the operation with OpenKeychain auth, we need to issue several requests // to the OpenKeychain API. IdentityBuild will take care of launching the relevant intents, // we just need to keep calling it until it returns a completed ApiIdentity. - if (connectionMode == ConnectionMode.OpenKeychain && identity == null) { + if (GitSettings.connectionMode == ConnectionMode.OpenKeychain && identity == null) { // Lazy initialization of the IdentityBuilder if (identityBuilder == null) { identityBuilder = SshApiSessionFactory.IdentityBuilder(this) @@ -199,7 +87,7 @@ abstract class BaseGitActivity : AppCompatActivity() { val localDir = requireNotNull(PasswordRepository.getRepositoryDirectory()) val op = when (operation) { - REQUEST_CLONE, GitOperation.GET_SSH_KEY_FROM_CLONE -> CloneOperation(localDir, url!!, this) + REQUEST_CLONE, GitOperation.GET_SSH_KEY_FROM_CLONE -> CloneOperation(localDir, GitSettings.url!!, this) REQUEST_PULL -> PullOperation(localDir, this) REQUEST_PUSH -> PushOperation(localDir, this) REQUEST_SYNC -> SyncOperation(localDir, this) @@ -213,7 +101,7 @@ abstract class BaseGitActivity : AppCompatActivity() { return } } - op.executeAfterAuthentication(connectionMode, serverUser, identity) + op.executeAfterAuthentication(GitSettings.connectionMode, identity) } catch (e: Exception) { e.printStackTrace() MaterialAlertDialogBuilder(this).setMessage(e.message).show() diff --git a/app/src/main/java/com/zeapo/pwdstore/git/GitConfigActivity.kt b/app/src/main/java/com/zeapo/pwdstore/git/GitConfigActivity.kt index e84c87e1..5138b50b 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/GitConfigActivity.kt +++ b/app/src/main/java/com/zeapo/pwdstore/git/GitConfigActivity.kt @@ -7,15 +7,14 @@ package com.zeapo.pwdstore.git import android.os.Bundle import android.os.Handler import android.util.Patterns -import androidx.core.content.edit import androidx.core.os.postDelayed import androidx.lifecycle.lifecycleScope import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar import com.zeapo.pwdstore.R import com.zeapo.pwdstore.databinding.ActivityGitConfigBinding +import com.zeapo.pwdstore.git.config.GitSettings import com.zeapo.pwdstore.utils.PasswordRepository -import com.zeapo.pwdstore.utils.PreferenceKeys import com.zeapo.pwdstore.utils.viewBinding import kotlinx.coroutines.launch import org.eclipse.jgit.lib.Constants @@ -29,16 +28,16 @@ class GitConfigActivity : BaseGitActivity() { setContentView(binding.root) supportActionBar?.setDisplayHomeAsUpEnabled(true) - if (username.isEmpty()) + if (GitSettings.authorName.isEmpty()) binding.gitUserName.requestFocus() else - binding.gitUserName.setText(username) - binding.gitUserEmail.setText(email) + binding.gitUserName.setText(GitSettings.authorName) + binding.gitUserEmail.setText(GitSettings.authorEmail) val repo = PasswordRepository.getRepository(PasswordRepository.getRepositoryDirectory()) if (repo != null) { try { val objectId = repo.resolve(Constants.HEAD) - val ref = repo.getRef("refs/heads/$branch") + val ref = repo.getRef("refs/heads/${GitSettings.branch}") val head = if (ref.objectId.equals(objectId)) ref.name else "DETACHED" binding.gitCommitHash.text = String.format("%s (%s)", objectId.abbreviate(8).name(), head) @@ -60,12 +59,10 @@ class GitConfigActivity : BaseGitActivity() { .setPositiveButton(getString(R.string.dialog_ok), null) .show() } else { - settings.edit { - putString(PreferenceKeys.GIT_CONFIG_USER_EMAIL, email) - putString(PreferenceKeys.GIT_CONFIG_USER_NAME, name) - } - PasswordRepository.setUserName(name) - PasswordRepository.setUserEmail(email) + GitSettings.authorEmail = email + GitSettings.authorName = name + PasswordRepository.setGitAuthorEmail(email) + PasswordRepository.setGitAuthorName(name) Snackbar.make(binding.root, getString(R.string.git_server_config_save_success), Snackbar.LENGTH_SHORT).show() Handler().postDelayed(500) { finish() } } diff --git a/app/src/main/java/com/zeapo/pwdstore/git/GitOperationActivity.kt b/app/src/main/java/com/zeapo/pwdstore/git/GitOperationActivity.kt index 121a3402..eda2a8fe 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/GitOperationActivity.kt +++ b/app/src/main/java/com/zeapo/pwdstore/git/GitOperationActivity.kt @@ -12,6 +12,7 @@ import androidx.lifecycle.lifecycleScope import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.zeapo.pwdstore.R import com.zeapo.pwdstore.UserPreference +import com.zeapo.pwdstore.git.config.GitSettings import com.zeapo.pwdstore.utils.PasswordRepository import kotlinx.coroutines.launch @@ -57,7 +58,7 @@ open class GitOperationActivity : BaseGitActivity() { * @param operation the operation to execute can be REQUEST_PULL or REQUEST_PUSH */ private suspend fun syncRepository(operation: Int) { - if (serverUser.isEmpty() || serverHostname.isEmpty() || url.isNullOrEmpty()) + if (GitSettings.url.isNullOrEmpty()) MaterialAlertDialogBuilder(this) .setMessage(getString(R.string.set_information_dialog_text)) .setPositiveButton(getString(R.string.dialog_positive)) { _, _ -> @@ -72,7 +73,7 @@ open class GitOperationActivity : BaseGitActivity() { .show() else { // check that the remote origin is here, else add it - PasswordRepository.addRemote("origin", url!!, true) + PasswordRepository.addRemote("origin", GitSettings.url!!, true) launchGitOperation(operation) } } diff --git a/app/src/main/java/com/zeapo/pwdstore/git/GitServerConfigActivity.kt b/app/src/main/java/com/zeapo/pwdstore/git/GitServerConfigActivity.kt index 3fe0314a..4d744821 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/GitServerConfigActivity.kt +++ b/app/src/main/java/com/zeapo/pwdstore/git/GitServerConfigActivity.kt @@ -7,18 +7,16 @@ package com.zeapo.pwdstore.git import android.os.Bundle import android.os.Handler import android.view.View -import androidx.core.content.edit import androidx.core.os.postDelayed -import androidx.core.widget.doOnTextChanged import androidx.lifecycle.lifecycleScope import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar import com.zeapo.pwdstore.R import com.zeapo.pwdstore.databinding.ActivityGitCloneBinding import com.zeapo.pwdstore.git.config.ConnectionMode +import com.zeapo.pwdstore.git.config.GitSettings import com.zeapo.pwdstore.git.config.Protocol import com.zeapo.pwdstore.utils.PasswordRepository -import com.zeapo.pwdstore.utils.PreferenceKeys import com.zeapo.pwdstore.utils.viewBinding import java.io.IOException import kotlinx.coroutines.launch @@ -40,22 +38,22 @@ class GitServerConfigActivity : BaseGitActivity() { setContentView(binding.root) supportActionBar?.setDisplayHomeAsUpEnabled(true) - binding.cloneProtocolGroup.check(when (protocol) { + binding.cloneProtocolGroup.check(when (GitSettings.protocol) { Protocol.Ssh -> R.id.clone_protocol_ssh Protocol.Https -> R.id.clone_protocol_https }) binding.cloneProtocolGroup.addOnButtonCheckedListener { _, checkedId, checked -> if (checked) { when (checkedId) { - R.id.clone_protocol_https -> protocol = Protocol.Https - R.id.clone_protocol_ssh -> protocol = Protocol.Ssh + R.id.clone_protocol_https -> GitSettings.protocol = Protocol.Https + R.id.clone_protocol_ssh -> GitSettings.protocol = Protocol.Ssh } updateConnectionModeToggleGroup() } } binding.connectionModeGroup.apply { - when (connectionMode) { + when (GitSettings.connectionMode) { ConnectionMode.SshKey -> check(R.id.connection_mode_ssh_key) ConnectionMode.Password -> check(R.id.connection_mode_password) ConnectionMode.OpenKeychain -> check(R.id.connection_mode_open_keychain) @@ -63,82 +61,37 @@ class GitServerConfigActivity : BaseGitActivity() { } addOnButtonCheckedListener { _, _, _ -> when (checkedButtonId) { - R.id.connection_mode_ssh_key -> connectionMode = ConnectionMode.SshKey - R.id.connection_mode_open_keychain -> connectionMode = ConnectionMode.OpenKeychain - R.id.connection_mode_password -> connectionMode = ConnectionMode.Password - View.NO_ID -> connectionMode = ConnectionMode.None + R.id.connection_mode_ssh_key -> GitSettings.connectionMode = ConnectionMode.SshKey + R.id.connection_mode_open_keychain -> GitSettings.connectionMode = ConnectionMode.OpenKeychain + R.id.connection_mode_password -> GitSettings.connectionMode = ConnectionMode.Password + View.NO_ID -> GitSettings.connectionMode = ConnectionMode.None } } } updateConnectionModeToggleGroup() - binding.serverUrl.apply { - setText(serverHostname) - doOnTextChanged { text, _, _, _ -> - serverHostname = text.toString().trim() - } - } - - binding.serverPort.apply { - setText(serverPort) - doOnTextChanged { text, _, _, _ -> - serverPort = text.toString().trim() - } - } - - binding.serverUser.apply { - if (serverUser.isEmpty()) { - requestFocus() - } else { - setText(serverUser) - } - doOnTextChanged { text, _, _, _ -> - serverUser = text.toString().trim() - } - } - - binding.serverPath.apply { - setText(serverPath) - doOnTextChanged { text, _, _, _ -> - serverPath = text.toString().trim() - } - } - - binding.serverBranch.apply { - setText(branch) - doOnTextChanged { text, _, _, _ -> - branch = text.toString().trim() - } - } + binding.serverUrl.setText(GitSettings.url) + binding.serverBranch.setText(GitSettings.branch) binding.saveButton.setOnClickListener { if (isClone && PasswordRepository.getRepository(null) == null) PasswordRepository.initialize() - when (val result = updateUrl()) { - GitUpdateUrlResult.Ok -> { - settings.edit { - putString(PreferenceKeys.GIT_REMOTE_PROTOCOL, protocol.pref) - putString(PreferenceKeys.GIT_REMOTE_AUTH, connectionMode.pref) - putString(PreferenceKeys.GIT_REMOTE_SERVER, serverHostname) - putString(PreferenceKeys.GIT_REMOTE_PORT, serverPort) - putString(PreferenceKeys.GIT_REMOTE_USERNAME, serverUser) - putString(PreferenceKeys.GIT_REMOTE_LOCATION, serverPath) - putString(PreferenceKeys.GIT_BRANCH_NAME, branch) - } - if (!isClone) { - Snackbar.make(binding.root, getString(R.string.git_server_config_save_success), Snackbar.LENGTH_SHORT).show() - Handler().postDelayed(500) { finish() } - } else { - cloneRepository() - } + GitSettings.branch = binding.serverBranch.text.toString().trim() + if (GitSettings.updateUrlIfValid(binding.serverUrl.text.toString().trim())) { + if (!isClone) { + Snackbar.make(binding.root, getString(R.string.git_server_config_save_success), Snackbar.LENGTH_SHORT).show() + Handler().postDelayed(500) { finish() } + } else { + cloneRepository() } - else -> Snackbar.make(binding.root, getString(R.string.git_server_config_save_error_prefix, getString(result.textRes)), Snackbar.LENGTH_LONG).show() + } else { + Snackbar.make(binding.root, getString(R.string.git_server_config_save_error), Snackbar.LENGTH_LONG).show() } } } private fun updateConnectionModeToggleGroup() { - if (protocol == Protocol.Ssh) { + if (GitSettings.protocol == Protocol.Ssh) { // Reset connection mode to SSH key if the current value (none) is not valid for SSH if (binding.connectionModeGroup.checkedButtonIds.isEmpty()) binding.connectionModeGroup.check(R.id.connection_mode_ssh_key) @@ -150,7 +103,7 @@ class GitServerConfigActivity : BaseGitActivity() { // Reset connection mode to password if the current value is not valid for HTTPS // Important note: This has to happen before disabling the other toggle buttons or they // won't uncheck. - if (connectionMode !in listOf(ConnectionMode.None, ConnectionMode.Password)) + if (GitSettings.connectionMode !in listOf(ConnectionMode.None, ConnectionMode.Password)) binding.connectionModeGroup.check(R.id.connection_mode_password) binding.connectionModeSshKey.isEnabled = false binding.connectionModeOpenKeychain.isEnabled = false diff --git a/app/src/main/java/com/zeapo/pwdstore/git/config/ConnectionMode.kt b/app/src/main/java/com/zeapo/pwdstore/git/config/ConnectionMode.kt deleted file mode 100644 index 6c4524c9..00000000 --- a/app/src/main/java/com/zeapo/pwdstore/git/config/ConnectionMode.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ -package com.zeapo.pwdstore.git.config - -enum class ConnectionMode(val pref: String) { - SshKey("ssh-key"), - Password("username/password"), - OpenKeychain("OpenKeychain"), - None("None"), - ; - - companion object { - - private val map = values().associateBy(ConnectionMode::pref) - fun fromString(type: String?): ConnectionMode { - return map[type ?: return SshKey] - ?: throw IllegalArgumentException("$type is not a valid ConnectionMode") - } - } -} diff --git a/app/src/main/java/com/zeapo/pwdstore/git/config/GitSettings.kt b/app/src/main/java/com/zeapo/pwdstore/git/config/GitSettings.kt new file mode 100644 index 00000000..b81a8f8f --- /dev/null +++ b/app/src/main/java/com/zeapo/pwdstore/git/config/GitSettings.kt @@ -0,0 +1,115 @@ +/* + * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved. + * SPDX-License-Identifier: GPL-3.0-only + */ +package com.zeapo.pwdstore.git.config + +import androidx.core.content.edit +import com.zeapo.pwdstore.Application +import com.zeapo.pwdstore.utils.PasswordRepository +import com.zeapo.pwdstore.utils.PreferenceKeys +import com.zeapo.pwdstore.utils.getEncryptedPrefs +import com.zeapo.pwdstore.utils.getString +import com.zeapo.pwdstore.utils.sharedPrefs +import java.io.File +import org.eclipse.jgit.transport.URIish + +enum class Protocol(val pref: String) { + Ssh("ssh://"), + Https("https://"), + ; + + companion object { + + private val map = values().associateBy(Protocol::pref) + fun fromString(type: String?): Protocol { + return map[type ?: return Ssh] + ?: throw IllegalArgumentException("$type is not a valid Protocol") + } + } +} + +enum class ConnectionMode(val pref: String) { + SshKey("ssh-key"), + Password("username/password"), + OpenKeychain("OpenKeychain"), + None("None"), + ; + + companion object { + + private val map = values().associateBy(ConnectionMode::pref) + fun fromString(type: String?): ConnectionMode { + return map[type ?: return SshKey] + ?: throw IllegalArgumentException("$type is not a valid ConnectionMode") + } + } +} + +object GitSettings { + + private const val DEFAULT_BRANCH = "master" + + private val settings by lazy { Application.instance.sharedPrefs } + private val encryptedSettings by lazy { Application.instance.getEncryptedPrefs("git_operation") } + + var protocol + get() = Protocol.fromString(settings.getString(PreferenceKeys.GIT_REMOTE_PROTOCOL)) + set(value) { + settings.edit { + putString(PreferenceKeys.GIT_REMOTE_PROTOCOL, value.pref) + } + } + var connectionMode + get() = ConnectionMode.fromString(settings.getString(PreferenceKeys.GIT_REMOTE_AUTH)) + set(value) { + settings.edit { + putString(PreferenceKeys.GIT_REMOTE_AUTH, value.pref) + } + } + var url + get() = settings.getString(PreferenceKeys.GIT_REMOTE_URL) + private set(value) { + require(value != null) + settings.edit { + putString(PreferenceKeys.GIT_REMOTE_URL, value) + } + if (PasswordRepository.isInitialized) + PasswordRepository.addRemote("origin", value, true) + // When the server changes, remote password and host key file should be deleted. + encryptedSettings.edit { remove(PreferenceKeys.HTTPS_PASSWORD) } + File("${Application.instance.filesDir}/.host_key").delete() + } + var authorName + get() = settings.getString(PreferenceKeys.GIT_CONFIG_AUTHOR_NAME) ?: "" + set(value) { + settings.edit { + putString(PreferenceKeys.GIT_CONFIG_AUTHOR_NAME, value) + } + } + var authorEmail + get() = settings.getString(PreferenceKeys.GIT_CONFIG_AUTHOR_EMAIL) ?: "" + set(value) { + settings.edit { + putString(PreferenceKeys.GIT_CONFIG_AUTHOR_EMAIL, value) + } + } + var branch + get() = settings.getString(PreferenceKeys.GIT_BRANCH_NAME) ?: DEFAULT_BRANCH + set(value) { + settings.edit { + putString(PreferenceKeys.GIT_BRANCH_NAME, value) + } + } + + fun updateUrlIfValid(newUrl: String): Boolean { + try { + URIish(newUrl) + } catch (_: Exception) { + return false + } + if (newUrl != url) + url = newUrl + return true + } +} diff --git a/app/src/main/java/com/zeapo/pwdstore/git/config/Protocol.kt b/app/src/main/java/com/zeapo/pwdstore/git/config/Protocol.kt deleted file mode 100644 index 5e172c76..00000000 --- a/app/src/main/java/com/zeapo/pwdstore/git/config/Protocol.kt +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright © 2014-2020 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ -package com.zeapo.pwdstore.git.config - -enum class Protocol(val pref: String) { - Ssh("ssh://"), - Https("https://"), - ; - - companion object { - - private val map = values().associateBy(Protocol::pref) - fun fromString(type: String?): Protocol { - return map[type ?: return Ssh] - ?: throw IllegalArgumentException("$type is not a valid Protocol") - } - } -} diff --git a/app/src/main/java/com/zeapo/pwdstore/git/config/SshApiSessionFactory.java b/app/src/main/java/com/zeapo/pwdstore/git/config/SshApiSessionFactory.java index aa62fdae..03760741 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/config/SshApiSessionFactory.java +++ b/app/src/main/java/com/zeapo/pwdstore/git/config/SshApiSessionFactory.java @@ -18,18 +18,12 @@ import com.jcraft.jsch.Identity; import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; -import com.jcraft.jsch.UserInfo; import com.zeapo.pwdstore.R; import com.zeapo.pwdstore.git.BaseGitActivity; import com.zeapo.pwdstore.utils.PreferenceKeys; -import org.eclipse.jgit.errors.UnsupportedCredentialItem; -import org.eclipse.jgit.transport.CredentialItem; -import org.eclipse.jgit.transport.CredentialsProvider; -import org.eclipse.jgit.transport.CredentialsProviderUserInfo; import org.eclipse.jgit.transport.JschConfigSessionFactory; import org.eclipse.jgit.transport.OpenSshConfig; -import org.eclipse.jgit.transport.URIish; import org.eclipse.jgit.util.Base64; import org.eclipse.jgit.util.FS; import org.openintents.ssh.authentication.ISshAuthenticationService; @@ -52,11 +46,9 @@ public class SshApiSessionFactory extends JschConfigSessionFactory { */ public static final int POST_SIGNATURE = 301; - private final String username; private final Identity identity; - public SshApiSessionFactory(String username, Identity identity) { - this.username = username; + public SshApiSessionFactory(Identity identity) { this.identity = identity; } @@ -74,32 +66,6 @@ public class SshApiSessionFactory extends JschConfigSessionFactory { protected void configure(@NonNull OpenSshConfig.Host hc, Session session) { session.setConfig("StrictHostKeyChecking", "no"); session.setConfig("PreferredAuthentications", "publickey"); - - CredentialsProvider provider = - new CredentialsProvider() { - @Override - public boolean isInteractive() { - return false; - } - - @Override - public boolean supports(CredentialItem... items) { - return true; - } - - @Override - public boolean get(URIish uri, CredentialItem... items) - throws UnsupportedCredentialItem { - for (CredentialItem item : items) { - if (item instanceof CredentialItem.Username) { - ((CredentialItem.Username) item).setValue(username); - } - } - return true; - } - }; - UserInfo userInfo = new CredentialsProviderUserInfo(session, provider); - session.setUserInfo(userInfo); } /** diff --git a/app/src/main/java/com/zeapo/pwdstore/git/config/SshjSessionFactory.kt b/app/src/main/java/com/zeapo/pwdstore/git/config/SshjSessionFactory.kt index c35e4a79..066f8ac6 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/config/SshjSessionFactory.kt +++ b/app/src/main/java/com/zeapo/pwdstore/git/config/SshjSessionFactory.kt @@ -95,10 +95,10 @@ abstract class InteractivePasswordFinder : PasswordFinder { final override fun shouldRetry(resource: Resource<*>?) = true } -class SshjSessionFactory(private val username: String, private val authData: SshAuthData, private val hostKeyFile: File) : SshSessionFactory() { +class SshjSessionFactory(private val authData: SshAuthData, private val hostKeyFile: File) : SshSessionFactory() { override fun getSession(uri: URIish, credentialsProvider: CredentialsProvider?, fs: FS?, tms: Int): RemoteSession { - return SshjSession(uri, username, authData, hostKeyFile).connect() + return SshjSession(uri, uri.user, authData, hostKeyFile).connect() } fun clearCredentials() { diff --git a/app/src/main/java/com/zeapo/pwdstore/git/operation/GitOperation.kt b/app/src/main/java/com/zeapo/pwdstore/git/operation/GitOperation.kt index 591c121f..ad8d318a 100644 --- a/app/src/main/java/com/zeapo/pwdstore/git/operation/GitOperation.kt +++ b/app/src/main/java/com/zeapo/pwdstore/git/operation/GitOperation.kt @@ -15,6 +15,7 @@ import com.zeapo.pwdstore.R import com.zeapo.pwdstore.UserPreference import com.zeapo.pwdstore.git.ErrorMessages import com.zeapo.pwdstore.git.config.ConnectionMode +import com.zeapo.pwdstore.git.config.GitSettings import com.zeapo.pwdstore.git.config.InteractivePasswordFinder import com.zeapo.pwdstore.git.config.SshApiSessionFactory import com.zeapo.pwdstore.git.config.SshAuthData @@ -47,18 +48,15 @@ abstract class GitOperation(gitDir: File, internal val callingActivity: Fragment private val hostKeyFile = callingActivity.filesDir.resolve(".host_key") protected val repository = PasswordRepository.getRepository(gitDir) protected val git = Git(repository) - protected val remoteBranch = PreferenceManager - .getDefaultSharedPreferences(callingActivity.applicationContext) - .getString(PreferenceKeys.GIT_BRANCH_NAME, "master") + protected val remoteBranch = GitSettings.branch - private class PasswordFinderCredentialsProvider(private val username: String, private val passwordFinder: PasswordFinder) : CredentialsProvider() { + private class PasswordFinderCredentialsProvider(private val passwordFinder: PasswordFinder) : CredentialsProvider() { override fun isInteractive() = true override fun get(uri: URIish?, vararg items: CredentialItem): Boolean { for (item in items) { when (item) { - is CredentialItem.Username -> item.value = username is CredentialItem.Password -> item.value = passwordFinder.reqPassword(null) else -> UnsupportedCredentialItem(uri, item.javaClass.name) } @@ -67,26 +65,26 @@ abstract class GitOperation(gitDir: File, internal val callingActivity: Fragment } override fun supports(vararg items: CredentialItem) = items.all { - it is CredentialItem.Username || it is CredentialItem.Password + it is CredentialItem.Password } } - private fun withPasswordAuthentication(username: String, passwordFinder: InteractivePasswordFinder): GitOperation { - val sessionFactory = SshjSessionFactory(username, SshAuthData.Password(passwordFinder), hostKeyFile) + private fun withPasswordAuthentication(passwordFinder: InteractivePasswordFinder): GitOperation { + val sessionFactory = SshjSessionFactory(SshAuthData.Password(passwordFinder), hostKeyFile) SshSessionFactory.setInstance(sessionFactory) - this.provider = PasswordFinderCredentialsProvider(username, passwordFinder) + this.provider = PasswordFinderCredentialsProvider(passwordFinder) return this } - private fun withPublicKeyAuthentication(username: String, passphraseFinder: InteractivePasswordFinder): GitOperation { - val sessionFactory = SshjSessionFactory(username, SshAuthData.PublicKeyFile(sshKeyFile, passphraseFinder), hostKeyFile) + private fun withPublicKeyAuthentication(passphraseFinder: InteractivePasswordFinder): GitOperation { + val sessionFactory = SshjSessionFactory(SshAuthData.PublicKeyFile(sshKeyFile, passphraseFinder), hostKeyFile) SshSessionFactory.setInstance(sessionFactory) this.provider = null return this } - private fun withOpenKeychainAuthentication(username: String, identity: SshApiSessionFactory.ApiIdentity?): GitOperation { - SshSessionFactory.setInstance(SshApiSessionFactory(username, identity)) + private fun withOpenKeychainAuthentication(identity: SshApiSessionFactory.ApiIdentity?): GitOperation { + SshSessionFactory.setInstance(SshApiSessionFactory(identity)) this.provider = null return this } @@ -117,7 +115,6 @@ abstract class GitOperation(gitDir: File, internal val callingActivity: Fragment suspend fun executeAfterAuthentication( connectionMode: ConnectionMode, - username: String, identity: SshApiSessionFactory.ApiIdentity? ) { when (connectionMode) { @@ -136,12 +133,12 @@ abstract class GitOperation(gitDir: File, internal val callingActivity: Fragment callingActivity.finish() }.show() } else { - withPublicKeyAuthentication(username, CredentialFinder(callingActivity, - connectionMode)).execute() + withPublicKeyAuthentication( + CredentialFinder(callingActivity, connectionMode)).execute() } - ConnectionMode.OpenKeychain -> withOpenKeychainAuthentication(username, identity).execute() + ConnectionMode.OpenKeychain -> withOpenKeychainAuthentication(identity).execute() ConnectionMode.Password -> withPasswordAuthentication( - username, CredentialFinder(callingActivity, connectionMode)).execute() + CredentialFinder(callingActivity, connectionMode)).execute() ConnectionMode.None -> execute() } } diff --git a/app/src/main/java/com/zeapo/pwdstore/pwgenxkpwd/XkpwdDictionary.kt b/app/src/main/java/com/zeapo/pwdstore/pwgenxkpwd/XkpwdDictionary.kt index 3edaa5da..2d0fbee9 100644 --- a/app/src/main/java/com/zeapo/pwdstore/pwgenxkpwd/XkpwdDictionary.kt +++ b/app/src/main/java/com/zeapo/pwdstore/pwgenxkpwd/XkpwdDictionary.kt @@ -5,9 +5,9 @@ package com.zeapo.pwdstore.pwgenxkpwd import android.content.Context -import androidx.preference.PreferenceManager import com.zeapo.pwdstore.R import com.zeapo.pwdstore.utils.PreferenceKeys +import com.zeapo.pwdstore.utils.getString import com.zeapo.pwdstore.utils.sharedPrefs import java.io.File @@ -17,7 +17,7 @@ class XkpwdDictionary(context: Context) { init { val prefs = context.sharedPrefs - val uri = prefs.getString(PreferenceKeys.PREF_KEY_CUSTOM_DICT, "")!! + val uri = prefs.getString(PreferenceKeys.PREF_KEY_CUSTOM_DICT) ?: "" val customDictFile = File(context.filesDir, XKPWD_CUSTOM_DICT_FILE) val lines = if (prefs.getBoolean(PreferenceKeys.PREF_KEY_IS_CUSTOM_DICT, false) && diff --git a/app/src/main/java/com/zeapo/pwdstore/sshkeygen/SshKeyGenActivity.kt b/app/src/main/java/com/zeapo/pwdstore/sshkeygen/SshKeyGenActivity.kt index d4d76738..98bda9d7 100644 --- a/app/src/main/java/com/zeapo/pwdstore/sshkeygen/SshKeyGenActivity.kt +++ b/app/src/main/java/com/zeapo/pwdstore/sshkeygen/SshKeyGenActivity.kt @@ -12,7 +12,6 @@ import androidx.appcompat.app.AppCompatActivity import androidx.core.content.edit import androidx.core.content.getSystemService import androidx.lifecycle.lifecycleScope -import androidx.preference.PreferenceManager import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.jcraft.jsch.JSch import com.jcraft.jsch.KeyPair diff --git a/app/src/main/java/com/zeapo/pwdstore/ui/adapters/PasswordItemRecyclerAdapter.kt b/app/src/main/java/com/zeapo/pwdstore/ui/adapters/PasswordItemRecyclerAdapter.kt index 9d5487ab..dc9d0ebd 100644 --- a/app/src/main/java/com/zeapo/pwdstore/ui/adapters/PasswordItemRecyclerAdapter.kt +++ b/app/src/main/java/com/zeapo/pwdstore/ui/adapters/PasswordItemRecyclerAdapter.kt @@ -10,7 +10,6 @@ import android.view.MotionEvent import android.view.View import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.AppCompatTextView -import androidx.preference.PreferenceManager import androidx.recyclerview.selection.ItemDetailsLookup import androidx.recyclerview.selection.Selection import androidx.recyclerview.widget.RecyclerView diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt b/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt index a3ddc170..e088097b 100644 --- a/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt +++ b/app/src/main/java/com/zeapo/pwdstore/utils/Extensions.kt @@ -101,6 +101,8 @@ fun Context.getEncryptedPrefs(fileName: String): SharedPreferences { val Context.sharedPrefs: SharedPreferences get() = PreferenceManager.getDefaultSharedPreferences(applicationContext) +fun SharedPreferences.getString(key: String): String? = getString(key, null) + suspend fun FragmentActivity.commitChange( message: String, finishWithResultOnEnd: Intent? = null, diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRepository.kt b/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRepository.kt index e07f30f2..0a918d0c 100644 --- a/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRepository.kt +++ b/app/src/main/java/com/zeapo/pwdstore/utils/PasswordRepository.kt @@ -39,8 +39,7 @@ open class PasswordRepository protected constructor() { @JvmStatic fun getSortOrder(settings: SharedPreferences): PasswordSortOrder { - return valueOf(settings.getString(PreferenceKeys.SORT_ORDER, null) - ?: FOLDER_FIRST.name) + return valueOf(settings.getString(PreferenceKeys.SORT_ORDER) ?: FOLDER_FIRST.name) } } } @@ -155,7 +154,7 @@ open class PasswordRepository protected constructor() { @JvmStatic fun getRepositoryDirectory(): File { return if (settings.getBoolean(PreferenceKeys.GIT_EXTERNAL, false)) { - val externalRepo = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO, null) + val externalRepo = settings.getString(PreferenceKeys.GIT_EXTERNAL_REPO) if (externalRepo != null) File(externalRepo) else @@ -239,7 +238,7 @@ open class PasswordRepository protected constructor() { * @param username username */ @JvmStatic - fun setUserName(username: String) { + fun setGitAuthorName(username: String) { setStringConfig("user", null, "name", username) } @@ -249,7 +248,7 @@ open class PasswordRepository protected constructor() { * @param email email */ @JvmStatic - fun setUserEmail(email: String) { + fun setGitAuthorEmail(email: String) { setStringConfig("user", null, "email", email) } diff --git a/app/src/main/java/com/zeapo/pwdstore/utils/PreferenceKeys.kt b/app/src/main/java/com/zeapo/pwdstore/utils/PreferenceKeys.kt index 24bf2096..05fb9326 100644 --- a/app/src/main/java/com/zeapo/pwdstore/utils/PreferenceKeys.kt +++ b/app/src/main/java/com/zeapo/pwdstore/utils/PreferenceKeys.kt @@ -23,16 +23,21 @@ object PreferenceKeys { const val FILTER_RECURSIVELY = "filter_recursively" const val GENERAL_SHOW_TIME = "general_show_time" const val GIT_CONFIG = "git_config" - const val GIT_CONFIG_USER_EMAIL = "git_config_user_email" - const val GIT_CONFIG_USER_NAME = "git_config_user_name" + const val GIT_CONFIG_AUTHOR_EMAIL = "git_config_user_email" + const val GIT_CONFIG_AUTHOR_NAME = "git_config_user_name" const val GIT_EXTERNAL = "git_external" const val GIT_EXTERNAL_REPO = "git_external_repo" const val GIT_REMOTE_AUTH = "git_remote_auth" + @Deprecated("Use GIT_REMOTE_URL instead") const val GIT_REMOTE_LOCATION = "git_remote_location" + @Deprecated("Use GIT_REMOTE_URL instead") const val GIT_REMOTE_PORT = "git_remote_port" const val GIT_REMOTE_PROTOCOL = "git_remote_protocol" const val GIT_DELETE_REPO = "git_delete_repo" + @Deprecated("Use GIT_REMOTE_URL instead") const val GIT_REMOTE_SERVER = "git_remote_server" + const val GIT_REMOTE_URL = "git_remote_url" + @Deprecated("Use GIT_REMOTE_URL instead") const val GIT_REMOTE_USERNAME = "git_remote_username" const val GIT_SERVER_INFO = "git_server_info" const val GIT_BRANCH_NAME = "git_branch" diff --git a/app/src/main/res/layout/activity_git_clone.xml b/app/src/main/res/layout/activity_git_clone.xml index f9bec00a..7c59572c 100644 --- a/app/src/main/res/layout/activity_git_clone.xml +++ b/app/src/main/res/layout/activity_git_clone.xml @@ -65,81 +65,21 @@ android:text="@string/clone_protocol_https" /> - - - - - + app:layout_constraintTop_toBottomOf="@id/clone_protocol_group"> - - - - - - - - - - - - @@ -150,10 +90,10 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_margin="8dp" - android:hint="Branch" + android:hint="@string/server_branch" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@id/label_server_path"> + app:layout_constraintTop_toBottomOf="@id/label_server_url"> إستخدام مجلد محلي الخادوم البروتوكول - عنوان الخادوم - 22 - مسار المستودع - إسم المستخدم نوع المصادقة diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml index 0ed0bcea..3c4df560 100644 --- a/app/src/main/res/values-cs/strings.xml +++ b/app/src/main/res/values-cs/strings.xml @@ -57,10 +57,6 @@ Vyberte kam ukládat hesla Server Protokol - URL serveru - 22 - Cesta k repozitáři - Jméno Mód ověření diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index cdfb7dba..fd94c8a9 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -40,10 +40,6 @@ Nutze lokalen Ordner Server Protokoll - Server URL - 22 - Repo-Pfad - Nutzername Authentifizierungsmethode diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 780e83ca..ff04cf3e 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -54,10 +54,6 @@ Servidor Protocolo - URL de servidor - 22 - Ruta del repositorio - Nombre de usuario Modo de autenticación diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 19fbdb33..9f9e1768 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -60,10 +60,6 @@ Serveur Protocole - URL du serveur - 22 - Chemin du dépôt - Nom d\'utilisateur Méthode d\'authentification diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index e0e84cda..132ecad4 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -41,10 +41,6 @@ ローカルディレクトリーを使用する サーバー プロトコル - サーバー URL - 22 - リポジトリのパス - ユーザー名 認証モード diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 8ed71353..79edf2d9 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -68,10 +68,6 @@ Você deve selecionar um diretório onde armazenar suas senhas. Se você deseja armazenar suas senhas dentro do armazenamento oculto do aplicativo, cancele esta caixa de diálogo e desative a opção \"Repositório Externo\". Servidor Protocolo - URL do servidor - Porta - Caminho do repositório - Usuário Modo de autenticação Usuário @@ -295,7 +291,6 @@ Chave SSH Senha Configuração salva com sucesso - Erro de configuração: %s hostname vazio por favor, verifique suas configurações e tente novamente porta deve ser numérica diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 7a46da75..596e1c14 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -62,10 +62,6 @@ Сервер Протокол - URL сервера - 22 - Путь к репозиторию - Имя пользователя Тип авторизации diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 343d5855..e7899c76 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -41,10 +41,6 @@ 使用本地目录 服务器 接口 - 服务器 URL - 22 - Repo 路径 - 用户名 认证模式 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index bbc9672a..572d67da 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -38,10 +38,6 @@ 使用本機目錄 伺服器 port - 伺服器 URL - 22 - Repo 路徑 - 使用者名稱 認證模式 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9251d91e..46dfa401 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -83,10 +83,8 @@ Server Protocol - Server URL - Port - Repo path - Username + Repository URL + Branch Authentication Mode @@ -326,7 +324,7 @@ Password OpenKeychain Successfully saved configuration - Configuration error: %s + The provided repository URL is not valid empty hostname please verify your settings and try again port must be numeric