Added password for ssh keys
This commit is contained in:
parent
b63614d68d
commit
da3f3c8935
@ -19,7 +19,11 @@ object CredentialsStateManager {
|
||||
|
||||
sealed class CredentialsState {
|
||||
object None : CredentialsState()
|
||||
object CredentialsRequested : CredentialsState()
|
||||
sealed class CredentialsRequested : CredentialsState()
|
||||
object SshCredentialsRequested : CredentialsRequested()
|
||||
object HttpCredentialsRequested : CredentialsRequested()
|
||||
object CredentialsDenied : CredentialsState()
|
||||
data class CredentialsAccepted(val user: String, val password: String) : CredentialsState()
|
||||
sealed class CredentialsAccepted : CredentialsState()
|
||||
data class SshCredentialsAccepted(val password: String) : CredentialsAccepted()
|
||||
data class HttpCredentialsAccepted(val user: String, val password: String) : CredentialsAccepted()
|
||||
}
|
@ -1,32 +1,67 @@
|
||||
package app.credentials
|
||||
|
||||
import org.apache.sshd.agent.SshAgent
|
||||
import org.apache.sshd.agent.local.AgentImpl
|
||||
import org.apache.sshd.agent.local.LocalAgentFactory
|
||||
import org.apache.sshd.client.SshClient
|
||||
import org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractive
|
||||
import org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractiveFactory
|
||||
import org.apache.sshd.client.auth.keyboard.UserInteraction
|
||||
import org.apache.sshd.client.auth.password.PasswordAuthenticationReporter
|
||||
import org.apache.sshd.client.auth.password.UserAuthPassword
|
||||
import org.apache.sshd.client.future.ConnectFuture
|
||||
import org.apache.sshd.client.session.ClientSession
|
||||
import org.apache.sshd.common.NamedResource
|
||||
import org.apache.sshd.common.config.keys.FilePasswordProvider
|
||||
import org.apache.sshd.common.keyprovider.FileKeyPairProvider
|
||||
import org.apache.sshd.common.session.SessionContext
|
||||
import org.eclipse.jgit.transport.RemoteSession
|
||||
import org.eclipse.jgit.transport.URIish
|
||||
import java.lang.Exception
|
||||
import java.security.KeyPair
|
||||
import java.time.Duration
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
|
||||
|
||||
private const val DEFAULT_SSH_PORT = 22
|
||||
|
||||
class GRemoteSession @Inject constructor(
|
||||
private val processProvider: Provider<GProcess>,
|
||||
): RemoteSession {
|
||||
) : RemoteSession {
|
||||
private val credentialsStateManager = CredentialsStateManager
|
||||
|
||||
private val client = SshClient.setUpDefaultClient()
|
||||
|
||||
private var connectFuture: ConnectFuture? = null
|
||||
|
||||
override fun exec(commandName: String, timeout: Int): Process {
|
||||
println(commandName)
|
||||
|
||||
val connectFuture = checkNotNull(connectFuture)
|
||||
|
||||
val session = connectFuture.clientSession
|
||||
session.auth().verify()
|
||||
|
||||
val auth = session.auth()
|
||||
auth.addListener { arg0 ->
|
||||
println("Authentication completed with " + if (arg0.isSuccess) "success" else "failure")
|
||||
}
|
||||
|
||||
session.waitFor(
|
||||
listOf(
|
||||
ClientSession.ClientSessionEvent.WAIT_AUTH,
|
||||
ClientSession.ClientSessionEvent.CLOSED,
|
||||
ClientSession.ClientSessionEvent.AUTHED
|
||||
), Duration.ofHours(2)
|
||||
)
|
||||
auth.verify()
|
||||
|
||||
val process = processProvider.get()
|
||||
process.setup(session, commandName)
|
||||
return process
|
||||
}
|
||||
|
||||
|
||||
override fun disconnect() {
|
||||
client.close()
|
||||
}
|
||||
@ -39,6 +74,25 @@ class GRemoteSession @Inject constructor(
|
||||
} else
|
||||
uri.port
|
||||
|
||||
val filePasswordProvider = object : FilePasswordProvider {
|
||||
override fun getPassword(session: SessionContext?, resourceKey: NamedResource?, retryIndex: Int): String? {
|
||||
credentialsStateManager.updateState(CredentialsState.SshCredentialsRequested)
|
||||
|
||||
var credentials = credentialsStateManager.currentCredentialsState
|
||||
while (credentials is CredentialsState.CredentialsRequested) {
|
||||
// TODO check if support for ED25519 with pwd can be added
|
||||
credentials = credentialsStateManager.currentCredentialsState
|
||||
}
|
||||
|
||||
return if(credentials !is CredentialsState.SshCredentialsAccepted)
|
||||
null
|
||||
else
|
||||
credentials.password
|
||||
}
|
||||
}
|
||||
|
||||
client.filePasswordProvider = filePasswordProvider
|
||||
|
||||
val connectFuture = client.connect(uri.user, uri.host, port)
|
||||
connectFuture.await()
|
||||
|
||||
|
@ -23,15 +23,14 @@ class HttpCredentialsProvider : CredentialsProvider() {
|
||||
}
|
||||
|
||||
override fun get(uri: URIish?, vararg items: CredentialItem?): Boolean {
|
||||
credentialsStateManager.updateState(CredentialsState.CredentialsRequested)
|
||||
credentialsStateManager.updateState(CredentialsState.HttpCredentialsRequested)
|
||||
|
||||
@Suppress("ControlFlowWithEmptyBody")
|
||||
var credentials = credentialsStateManager.currentCredentialsState
|
||||
while (credentials is CredentialsState.CredentialsRequested) {
|
||||
credentials = credentialsStateManager.currentCredentialsState
|
||||
}
|
||||
|
||||
if(credentials is CredentialsState.CredentialsAccepted) {
|
||||
if(credentials is CredentialsState.HttpCredentialsAccepted) {
|
||||
val userItem = items.firstOrNull { it?.promptText == "Username" }
|
||||
val passwordItem = items.firstOrNull { it?.promptText == "Password" }
|
||||
|
||||
|
@ -226,8 +226,12 @@ class GitManager @Inject constructor(
|
||||
credentialsStateManager.updateState(CredentialsState.CredentialsDenied)
|
||||
}
|
||||
|
||||
fun credentialsAccepted(user: String, password: String) {
|
||||
credentialsStateManager.updateState(CredentialsState.CredentialsAccepted(user, password))
|
||||
fun httpCredentialsAccepted(user: String, password: String) {
|
||||
credentialsStateManager.updateState(CredentialsState.HttpCredentialsAccepted(user, password))
|
||||
}
|
||||
|
||||
fun sshCredentialsAccepted(password: String) {
|
||||
credentialsStateManager.updateState(CredentialsState.SshCredentialsAccepted(password))
|
||||
}
|
||||
|
||||
suspend fun diffListFromCommit(commit: RevCommit): List<DiffEntry> {
|
||||
@ -284,6 +288,7 @@ class GitManager @Inject constructor(
|
||||
try {
|
||||
callback()
|
||||
} catch (ex: Exception) {
|
||||
ex.printStackTrace()
|
||||
errorsManager.addError(newErrorNow(ex, ex.localizedMessage))
|
||||
} finally {
|
||||
_processing.value = false
|
||||
|
@ -9,6 +9,7 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.diff.DiffEntry
|
||||
import org.eclipse.jgit.lib.Constants
|
||||
import javax.inject.Inject
|
||||
|
||||
class StatusManager @Inject constructor() {
|
||||
@ -68,6 +69,13 @@ class StatusManager @Inject constructor() {
|
||||
loadStatus(git)
|
||||
}
|
||||
|
||||
// suspend fun stageHunk(git: Git) {
|
||||
//// val repository = git.repository
|
||||
//// val objectInserter = repository.newObjectInserter()
|
||||
//
|
||||
//// objectInserter.insert(Constants.OBJ_BLOB,)
|
||||
// }
|
||||
|
||||
suspend fun unstage(git: Git, diffEntry: DiffEntry) = withContext(Dispatchers.IO) {
|
||||
git.reset()
|
||||
.addPath(diffEntry.filePath)
|
||||
|
@ -10,6 +10,7 @@ import app.credentials.CredentialsState
|
||||
import app.git.DiffEntryType
|
||||
import app.git.GitManager
|
||||
import app.ui.dialogs.NewBranchDialog
|
||||
import app.ui.dialogs.PasswordDialog
|
||||
import app.ui.dialogs.UserPasswordDialog
|
||||
import openRepositoryDialog
|
||||
import org.eclipse.jgit.revwalk.RevCommit
|
||||
@ -32,7 +33,7 @@ fun RepositoryOpenPage(gitManager: GitManager, dialogManager: DialogManager) {
|
||||
|
||||
val credentialsState by gitManager.credentialsState.collectAsState()
|
||||
|
||||
if (credentialsState == CredentialsState.CredentialsRequested) {
|
||||
if (credentialsState == CredentialsState.HttpCredentialsRequested) {
|
||||
dialogManager.show {
|
||||
UserPasswordDialog(
|
||||
onReject = {
|
||||
@ -40,7 +41,20 @@ fun RepositoryOpenPage(gitManager: GitManager, dialogManager: DialogManager) {
|
||||
dialogManager.dismiss()
|
||||
},
|
||||
onAccept = { user, password ->
|
||||
gitManager.credentialsAccepted(user, password)
|
||||
gitManager.httpCredentialsAccepted(user, password)
|
||||
dialogManager.dismiss()
|
||||
}
|
||||
)
|
||||
}
|
||||
} else if (credentialsState == CredentialsState.SshCredentialsRequested) {
|
||||
dialogManager.show {
|
||||
PasswordDialog(
|
||||
onReject = {
|
||||
gitManager.credentialsDenied()
|
||||
dialogManager.dismiss()
|
||||
},
|
||||
onAccept = { password ->
|
||||
gitManager.sshCredentialsAccepted(password)
|
||||
dialogManager.dismiss()
|
||||
}
|
||||
)
|
||||
|
83
src/main/kotlin/app/ui/dialogs/PasswordDialog.kt
Normal file
83
src/main/kotlin/app/ui/dialogs/PasswordDialog.kt
Normal file
@ -0,0 +1,83 @@
|
||||
package app.ui.dialogs
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusOrder
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
@Composable
|
||||
fun PasswordDialog(
|
||||
onReject: () -> Unit,
|
||||
onAccept: (password: String) -> Unit
|
||||
) {
|
||||
var passwordField by remember { mutableStateOf("") }
|
||||
val passwordFieldFocusRequester = remember { FocusRequester() }
|
||||
val buttonFieldFocusRequester = remember { FocusRequester() }
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colors.background),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
|
||||
Text(
|
||||
text = "Introduce your default SSH key's password",
|
||||
modifier = Modifier
|
||||
.padding(vertical = 8.dp),
|
||||
)
|
||||
OutlinedTextField(
|
||||
modifier = Modifier.padding(bottom = 8.dp)
|
||||
.focusOrder(passwordFieldFocusRequester) {
|
||||
this.next = buttonFieldFocusRequester
|
||||
}
|
||||
.width(300.dp),
|
||||
value = passwordField,
|
||||
singleLine = true,
|
||||
label = { Text("Password", fontSize = 14.sp) },
|
||||
textStyle = TextStyle(fontSize = 14.sp),
|
||||
onValueChange = {
|
||||
passwordField = it
|
||||
},
|
||||
visualTransformation = PasswordVisualTransformation()
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(top = 16.dp)
|
||||
.align(Alignment.End)
|
||||
) {
|
||||
TextButton(
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
onClick = {
|
||||
onReject()
|
||||
}
|
||||
) {
|
||||
Text("Cancel")
|
||||
}
|
||||
Button(
|
||||
modifier = Modifier.focusOrder(buttonFieldFocusRequester) {
|
||||
this.previous = passwordFieldFocusRequester
|
||||
},
|
||||
onClick = {
|
||||
onAccept(passwordField)
|
||||
}
|
||||
) {
|
||||
Text("Ok")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
passwordFieldFocusRequester.requestFocus()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user