Added network credentials management
This commit is contained in:
parent
54f013d291
commit
8db1f183ff
@ -1,3 +1,5 @@
|
|||||||
|
import credentials.CredentialsState
|
||||||
|
import credentials.CredentialsStateManager
|
||||||
import git.*
|
import git.*
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
@ -18,6 +20,7 @@ class GitManager {
|
|||||||
private val branchesManager = BranchesManager()
|
private val branchesManager = BranchesManager()
|
||||||
private val stashManager = StashManager()
|
private val stashManager = StashManager()
|
||||||
private val diffManager = DiffManager()
|
private val diffManager = DiffManager()
|
||||||
|
private val credentialsStateManager = CredentialsStateManager
|
||||||
|
|
||||||
private val managerScope = CoroutineScope(SupervisorJob())
|
private val managerScope = CoroutineScope(SupervisorJob())
|
||||||
|
|
||||||
@ -51,6 +54,9 @@ class GitManager {
|
|||||||
val latestDirectoryOpened: File?
|
val latestDirectoryOpened: File?
|
||||||
get() = File(preferences.latestOpenedRepositoryPath).parentFile
|
get() = File(preferences.latestOpenedRepositoryPath).parentFile
|
||||||
|
|
||||||
|
val credentialsState: StateFlow<CredentialsState>
|
||||||
|
get() = credentialsStateManager.credentialsState
|
||||||
|
|
||||||
private var git: Git? = null
|
private var git: Git? = null
|
||||||
|
|
||||||
val safeGit: Git
|
val safeGit: Git
|
||||||
@ -172,6 +178,14 @@ class GitManager {
|
|||||||
fun statusShouldBeUpdated() {
|
fun statusShouldBeUpdated() {
|
||||||
_lastTimeChecked.value = System.currentTimeMillis()
|
_lastTimeChecked.value = System.currentTimeMillis()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun credentialsDenied() {
|
||||||
|
credentialsStateManager.updateState(CredentialsState.CredentialsDenied)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun credentialsAccepted(user: String, password: String) {
|
||||||
|
credentialsStateManager.updateState(CredentialsState.CredentialsAccepted(user, password))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
import androidx.compose.animation.Crossfade
|
import androidx.compose.animation.Crossfade
|
||||||
import androidx.compose.foundation.layout.*
|
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.runtime.*
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.unit.dp
|
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.api.Git
|
||||||
import org.eclipse.jgit.diff.DiffEntry
|
import org.eclipse.jgit.diff.DiffEntry
|
||||||
import org.eclipse.jgit.lib.Repository
|
import org.eclipse.jgit.lib.Repository
|
||||||
@ -29,8 +36,45 @@ fun RepositorySelected(gitManager: GitManager, repository: Repository) {
|
|||||||
|
|
||||||
val selectedIndexCommitLog = remember { mutableStateOf(-1) }
|
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 {
|
Row {
|
||||||
Column (
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.widthIn(min = 300.dp)
|
.widthIn(min = 300.dp)
|
||||||
.weight(0.15f)
|
.weight(0.15f)
|
||||||
@ -58,7 +102,7 @@ fun RepositorySelected(gitManager: GitManager, repository: Repository) {
|
|||||||
} else
|
} else
|
||||||
commit.parents.first()
|
commit.parents.first()
|
||||||
|
|
||||||
val oldTreeParser = if(parent != null)
|
val oldTreeParser = if (parent != null)
|
||||||
prepareTreeParser(repository, parent)
|
prepareTreeParser(repository, parent)
|
||||||
else {
|
else {
|
||||||
CanonicalTreeParser()
|
CanonicalTreeParser()
|
||||||
|
@ -1,11 +1,25 @@
|
|||||||
package credentials
|
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>(CredentialsState.None)
|
||||||
|
val credentialsState: StateFlow<CredentialsState>
|
||||||
|
get() = _credentialsState
|
||||||
|
|
||||||
|
val currentCredentialsState: CredentialsState
|
||||||
|
get() = credentialsState.value
|
||||||
|
|
||||||
|
fun updateState(newCredentialsState: CredentialsState) {
|
||||||
|
_credentialsState.value = newCredentialsState
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class CredentialsState {
|
sealed class CredentialsState {
|
||||||
object CredentialsRequested: CredentialsState()
|
object None : CredentialsState()
|
||||||
object CredentialsDenied: CredentialsState()
|
object CredentialsRequested : CredentialsState()
|
||||||
data class CredentialsAccepted(val user: String, val password: String): CredentialsState()
|
object CredentialsDenied : CredentialsState()
|
||||||
|
data class CredentialsAccepted(val user: String, val password: String) : CredentialsState()
|
||||||
}
|
}
|
51
src/main/kotlin/credentials/HttpCredentialsProvider.kt
Normal file
51
src/main/kotlin/credentials/HttpCredentialsProvider.kt
Normal file
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,22 +1,42 @@
|
|||||||
package git
|
package git
|
||||||
|
|
||||||
|
import credentials.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.eclipse.jgit.api.Git
|
import org.eclipse.jgit.api.Git
|
||||||
import org.eclipse.jgit.transport.CredentialsProvider
|
import org.eclipse.jgit.transport.*
|
||||||
|
|
||||||
class RemoteOperationsManager {
|
class RemoteOperationsManager {
|
||||||
|
private val sessionManager = GSessionManager()
|
||||||
|
|
||||||
suspend fun pull(git: Git) = withContext(Dispatchers.IO) {
|
suspend fun pull(git: Git) = withContext(Dispatchers.IO) {
|
||||||
git
|
git
|
||||||
.pull()
|
.pull()
|
||||||
|
.setTransportConfigCallback {
|
||||||
|
if (it is SshTransport) {
|
||||||
|
it.sshSessionFactory = sessionManager.generateSshSessionFactory()
|
||||||
|
} else if (it is HttpTransport) {
|
||||||
|
it.credentialsProvider = HttpCredentialsProvider()
|
||||||
|
}
|
||||||
|
}
|
||||||
.setCredentialsProvider(CredentialsProvider.getDefault())
|
.setCredentialsProvider(CredentialsProvider.getDefault())
|
||||||
.call()
|
.call()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun push(git: Git) = withContext(Dispatchers.IO) {
|
suspend fun push(git: Git) = withContext(Dispatchers.IO) {
|
||||||
|
val currentBranchRefSpec = git.repository.fullBranch
|
||||||
|
|
||||||
git
|
git
|
||||||
.push()
|
.push()
|
||||||
|
.setRefSpecs(RefSpec(currentBranchRefSpec))
|
||||||
.setPushTags()
|
.setPushTags()
|
||||||
|
.setTransportConfigCallback {
|
||||||
|
if (it is SshTransport) {
|
||||||
|
it.sshSessionFactory = sessionManager.generateSshSessionFactory()
|
||||||
|
} else if (it is HttpTransport) {
|
||||||
|
it.credentialsProvider = HttpCredentialsProvider()
|
||||||
|
}
|
||||||
|
}
|
||||||
.call()
|
.call()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -112,6 +112,7 @@ fun GMenu(
|
|||||||
onPopStash: () -> Unit,
|
onPopStash: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val openHovering = remember { mutableStateOf(false) }
|
val openHovering = remember { mutableStateOf(false) }
|
||||||
|
val pullHovering = remember { mutableStateOf(false) }
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -130,8 +131,12 @@ fun GMenu(
|
|||||||
)
|
)
|
||||||
MenuButton(
|
MenuButton(
|
||||||
title = "Pull",
|
title = "Pull",
|
||||||
|
hovering = pullHovering,
|
||||||
icon = painterResource("download.svg"),
|
icon = painterResource("download.svg"),
|
||||||
onClick = onPull,
|
onClick = {
|
||||||
|
pullHovering.value = false
|
||||||
|
onPull()
|
||||||
|
},
|
||||||
enabled = isRepositoryOpen,
|
enabled = isRepositoryOpen,
|
||||||
)
|
)
|
||||||
MenuButton(
|
MenuButton(
|
||||||
|
Loading…
Reference in New Issue
Block a user