diff options
Diffstat (limited to 'app/src/main/java/com/zeapo/pwdstore/git')
10 files changed, 172 insertions, 297 deletions
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() } } |
