diff --git a/src/main/kotlin/GitManager.kt b/src/main/kotlin/GitManager.kt index d84d473..eacf747 100644 --- a/src/main/kotlin/GitManager.kt +++ b/src/main/kotlin/GitManager.kt @@ -1,3 +1,5 @@ +import credentials.CredentialsState +import credentials.CredentialsStateManager import git.* import kotlinx.coroutines.* import kotlinx.coroutines.flow.MutableStateFlow @@ -18,6 +20,7 @@ class GitManager { private val branchesManager = BranchesManager() private val stashManager = StashManager() private val diffManager = DiffManager() + private val credentialsStateManager = CredentialsStateManager private val managerScope = CoroutineScope(SupervisorJob()) @@ -51,6 +54,9 @@ class GitManager { val latestDirectoryOpened: File? get() = File(preferences.latestOpenedRepositoryPath).parentFile + val credentialsState: StateFlow + get() = credentialsStateManager.credentialsState + private var git: Git? = null val safeGit: Git @@ -172,6 +178,14 @@ class GitManager { fun statusShouldBeUpdated() { _lastTimeChecked.value = System.currentTimeMillis() } + + fun credentialsDenied() { + credentialsStateManager.updateState(CredentialsState.CredentialsDenied) + } + + fun credentialsAccepted(user: String, password: String) { + credentialsStateManager.updateState(CredentialsState.CredentialsAccepted(user, password)) + } } diff --git a/src/main/kotlin/RepositorySelected.kt b/src/main/kotlin/RepositorySelected.kt index ed53888..1a7a660 100644 --- a/src/main/kotlin/RepositorySelected.kt +++ b/src/main/kotlin/RepositorySelected.kt @@ -1,8 +1,15 @@ import androidx.compose.animation.Crossfade import androidx.compose.foundation.layout.* +import androidx.compose.material.Button +import androidx.compose.material.OutlinedTextField +import androidx.compose.material.Text import androidx.compose.runtime.* import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.Dialog +import credentials.CredentialsState import org.eclipse.jgit.api.Git import org.eclipse.jgit.diff.DiffEntry import org.eclipse.jgit.lib.Repository @@ -29,8 +36,45 @@ fun RepositorySelected(gitManager: GitManager, repository: Repository) { val selectedIndexCommitLog = remember { mutableStateOf(-1) } + val credentialsState by gitManager.credentialsState.collectAsState() + + if (credentialsState == CredentialsState.CredentialsRequested) { + var userField by remember { mutableStateOf("") } + var passwordField by remember { mutableStateOf("") } + Dialog( + onCloseRequest = { + gitManager.credentialsDenied() + }, + title = "Introduce your remote server credentials", + ) { + Column { + OutlinedTextField( + value = userField, + label = { Text("User", fontSize = 14.sp) }, + textStyle = TextStyle(fontSize = 14.sp), + onValueChange = { + userField = it + }, + ) + OutlinedTextField( + value = passwordField, + label = { Text("Password", fontSize = 14.sp) }, + textStyle = TextStyle(fontSize = 14.sp), + onValueChange = { + passwordField = it + }, + ) + Button(onClick = {gitManager.credentialsAccepted(userField, passwordField)}) { + Text("Ok") + } + } + } + } + + + Row { - Column ( + Column( modifier = Modifier .widthIn(min = 300.dp) .weight(0.15f) @@ -58,7 +102,7 @@ fun RepositorySelected(gitManager: GitManager, repository: Repository) { } else commit.parents.first() - val oldTreeParser = if(parent != null) + val oldTreeParser = if (parent != null) prepareTreeParser(repository, parent) else { CanonicalTreeParser() diff --git a/src/main/kotlin/credentials/CredentialsStateManager.kt b/src/main/kotlin/credentials/CredentialsStateManager.kt index 1acdea3..597240c 100644 --- a/src/main/kotlin/credentials/CredentialsStateManager.kt +++ b/src/main/kotlin/credentials/CredentialsStateManager.kt @@ -1,11 +1,25 @@ package credentials -class CredentialsStateManager { +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +//TODO Mark as a singleton when dagger is implemented +object CredentialsStateManager { + private val _credentialsState = MutableStateFlow(CredentialsState.None) + val credentialsState: StateFlow + get() = _credentialsState + + val currentCredentialsState: CredentialsState + get() = credentialsState.value + + fun updateState(newCredentialsState: CredentialsState) { + _credentialsState.value = newCredentialsState + } } sealed class CredentialsState { - object CredentialsRequested: CredentialsState() - object CredentialsDenied: CredentialsState() - data class CredentialsAccepted(val user: String, val password: String): CredentialsState() + object None : CredentialsState() + object CredentialsRequested : CredentialsState() + object CredentialsDenied : CredentialsState() + data class CredentialsAccepted(val user: String, val password: String) : CredentialsState() } \ No newline at end of file diff --git a/src/main/kotlin/credentials/HttpCredentialsProvider.kt b/src/main/kotlin/credentials/HttpCredentialsProvider.kt new file mode 100644 index 0000000..00076a1 --- /dev/null +++ b/src/main/kotlin/credentials/HttpCredentialsProvider.kt @@ -0,0 +1,51 @@ +package credentials + +import org.eclipse.jgit.transport.CredentialItem +import org.eclipse.jgit.transport.CredentialsProvider +import org.eclipse.jgit.transport.URIish + +class HttpCredentialsProvider : CredentialsProvider() { + private val credentialsStateManager = CredentialsStateManager + + override fun isInteractive(): Boolean { + return true + } + + override fun supports(vararg items: CredentialItem?): Boolean { + println(items) + val fields = items.map { credentialItem -> credentialItem?.promptText } + return if (fields.isEmpty()) { + true + } else + fields.size == 2 && + fields.contains("Username") && + fields.contains("Password") + } + + override fun get(uri: URIish?, vararg items: CredentialItem?): Boolean { + credentialsStateManager.updateState(CredentialsState.CredentialsRequested) + + @Suppress("ControlFlowWithEmptyBody") + var credentials = credentialsStateManager.currentCredentialsState + while (credentials is CredentialsState.CredentialsRequested) { + credentials = credentialsStateManager.currentCredentialsState + } + + if(credentials is CredentialsState.CredentialsAccepted) { + val userItem = items.firstOrNull { it?.promptText == "Username" } + val passwordItem = items.firstOrNull { it?.promptText == "Password" } + + if(userItem is CredentialItem.Username && + passwordItem is CredentialItem.Password) { + + userItem.value = credentials.user + passwordItem.value = credentials.password.toCharArray() + + return true + } + } + + return false + } + +} \ No newline at end of file diff --git a/src/main/kotlin/git/RemoteOperationsManager.kt b/src/main/kotlin/git/RemoteOperationsManager.kt index 07e9cd9..4e3f7c3 100644 --- a/src/main/kotlin/git/RemoteOperationsManager.kt +++ b/src/main/kotlin/git/RemoteOperationsManager.kt @@ -1,22 +1,42 @@ package git +import credentials.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.eclipse.jgit.api.Git -import org.eclipse.jgit.transport.CredentialsProvider +import org.eclipse.jgit.transport.* class RemoteOperationsManager { + private val sessionManager = GSessionManager() + suspend fun pull(git: Git) = withContext(Dispatchers.IO) { git .pull() + .setTransportConfigCallback { + if (it is SshTransport) { + it.sshSessionFactory = sessionManager.generateSshSessionFactory() + } else if (it is HttpTransport) { + it.credentialsProvider = HttpCredentialsProvider() + } + } .setCredentialsProvider(CredentialsProvider.getDefault()) .call() } suspend fun push(git: Git) = withContext(Dispatchers.IO) { + val currentBranchRefSpec = git.repository.fullBranch + git .push() + .setRefSpecs(RefSpec(currentBranchRefSpec)) .setPushTags() + .setTransportConfigCallback { + if (it is SshTransport) { + it.sshSessionFactory = sessionManager.generateSshSessionFactory() + } else if (it is HttpTransport) { + it.credentialsProvider = HttpCredentialsProvider() + } + } .call() } } \ No newline at end of file diff --git a/src/main/kotlin/main.kt b/src/main/kotlin/main.kt index 03a181b..e4c2610 100644 --- a/src/main/kotlin/main.kt +++ b/src/main/kotlin/main.kt @@ -112,6 +112,7 @@ fun GMenu( onPopStash: () -> Unit, ) { val openHovering = remember { mutableStateOf(false) } + val pullHovering = remember { mutableStateOf(false) } Row( modifier = Modifier @@ -130,8 +131,12 @@ fun GMenu( ) MenuButton( title = "Pull", + hovering = pullHovering, icon = painterResource("download.svg"), - onClick = onPull, + onClick = { + pullHovering.value = false + onPull() + }, enabled = isRepositoryOpen, ) MenuButton(