parent
96cbdba8d9
commit
7de332be87
@ -24,12 +24,15 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
val jgit = "6.4.0.202211300538-r"
|
||||||
|
|
||||||
implementation(compose.desktop.currentOs)
|
implementation(compose.desktop.currentOs)
|
||||||
@OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class)
|
@OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class)
|
||||||
implementation(compose.desktop.components.splitPane)
|
implementation(compose.desktop.components.splitPane)
|
||||||
implementation(compose("org.jetbrains.compose.ui:ui-util"))
|
implementation(compose("org.jetbrains.compose.ui:ui-util"))
|
||||||
implementation(compose("org.jetbrains.compose.components:components-animatedimage"))
|
implementation(compose("org.jetbrains.compose.components:components-animatedimage"))
|
||||||
implementation("org.eclipse.jgit:org.eclipse.jgit:6.4.0.202211300538-r")
|
implementation("org.eclipse.jgit:org.eclipse.jgit:$jgit")
|
||||||
|
implementation("org.eclipse.jgit:org.eclipse.jgit.gpg.bc:$jgit")
|
||||||
implementation("org.apache.sshd:sshd-core:2.9.0")
|
implementation("org.apache.sshd:sshd-core:2.9.0")
|
||||||
implementation("com.google.dagger:dagger:2.44.2")
|
implementation("com.google.dagger:dagger:2.44.2")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1")
|
||||||
|
@ -24,9 +24,11 @@ sealed class CredentialsState {
|
|||||||
object None : CredentialsState()
|
object None : CredentialsState()
|
||||||
sealed class CredentialsRequested : CredentialsState()
|
sealed class CredentialsRequested : CredentialsState()
|
||||||
object SshCredentialsRequested : CredentialsRequested()
|
object SshCredentialsRequested : CredentialsRequested()
|
||||||
|
object GpgCredentialsRequested : CredentialsRequested()
|
||||||
object HttpCredentialsRequested : CredentialsRequested()
|
object HttpCredentialsRequested : CredentialsRequested()
|
||||||
object CredentialsDenied : CredentialsState()
|
object CredentialsDenied : CredentialsState()
|
||||||
sealed class CredentialsAccepted : CredentialsState()
|
sealed class CredentialsAccepted : CredentialsState()
|
||||||
data class SshCredentialsAccepted(val password: String) : CredentialsAccepted()
|
data class SshCredentialsAccepted(val password: String) : CredentialsAccepted()
|
||||||
|
data class GpgCredentialsAccepted(val password: String) : CredentialsAccepted()
|
||||||
data class HttpCredentialsAccepted(val user: String, val password: String) : CredentialsAccepted()
|
data class HttpCredentialsAccepted(val user: String, val password: String) : CredentialsAccepted()
|
||||||
}
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package com.jetpackduba.gitnuro.credentials
|
||||||
|
|
||||||
|
import org.eclipse.jgit.transport.CredentialItem
|
||||||
|
import org.eclipse.jgit.transport.CredentialsProvider
|
||||||
|
import org.eclipse.jgit.transport.URIish
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private const val PASSWORD_FIELD_IDENTIFIER = "Passphrase"
|
||||||
|
|
||||||
|
class GpgCredentialsProvider @Inject constructor(
|
||||||
|
private val credentialsStateManager: CredentialsStateManager,
|
||||||
|
) : CredentialsProvider() {
|
||||||
|
override fun isInteractive(): Boolean = true
|
||||||
|
|
||||||
|
override fun supports(vararg items: CredentialItem?): Boolean {
|
||||||
|
println(items)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun get(uri: URIish?, vararg items: CredentialItem?): Boolean {
|
||||||
|
val item = items.firstOrNull {
|
||||||
|
it?.promptText == PASSWORD_FIELD_IDENTIFIER
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item != null && item is CredentialItem.Password) {
|
||||||
|
credentialsStateManager.updateState(CredentialsState.GpgCredentialsRequested)
|
||||||
|
|
||||||
|
var credentials = credentialsStateManager.currentCredentialsState
|
||||||
|
|
||||||
|
while (credentials is CredentialsState.GpgCredentialsRequested) {
|
||||||
|
credentials = credentialsStateManager.currentCredentialsState
|
||||||
|
}
|
||||||
|
|
||||||
|
if (credentials is CredentialsState.GpgCredentialsAccepted) {
|
||||||
|
item.value = credentials.password.toCharArray()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,17 +1,24 @@
|
|||||||
package com.jetpackduba.gitnuro.git.workspace
|
package com.jetpackduba.gitnuro.git.workspace
|
||||||
|
|
||||||
|
import com.jetpackduba.gitnuro.credentials.GpgCredentialsProvider
|
||||||
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.revwalk.RevCommit
|
import org.eclipse.jgit.revwalk.RevCommit
|
||||||
|
import org.eclipse.jgit.transport.CredentialItem
|
||||||
|
import org.eclipse.jgit.transport.CredentialsProvider
|
||||||
|
import org.eclipse.jgit.transport.URIish
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class DoCommitUseCase @Inject constructor() {
|
class DoCommitUseCase @Inject constructor(
|
||||||
|
private val gpgCredentialsProvider: GpgCredentialsProvider,
|
||||||
|
) {
|
||||||
suspend operator fun invoke(git: Git, message: String, amend: Boolean): RevCommit = withContext(Dispatchers.IO) {
|
suspend operator fun invoke(git: Git, message: String, amend: Boolean): RevCommit = withContext(Dispatchers.IO) {
|
||||||
git.commit()
|
git.commit()
|
||||||
.setMessage(message)
|
.setMessage(message)
|
||||||
.setAllowEmpty(amend) // Only allow empty commits when amending
|
.setAllowEmpty(amend) // Only allow empty commits when amending
|
||||||
.setAmend(amend)
|
.setAmend(amend)
|
||||||
|
.setCredentialsProvider(gpgCredentialsProvider)
|
||||||
.call()
|
.call()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -22,9 +22,7 @@ import androidx.compose.ui.unit.sp
|
|||||||
import com.jetpackduba.gitnuro.LoadingRepository
|
import com.jetpackduba.gitnuro.LoadingRepository
|
||||||
import com.jetpackduba.gitnuro.LocalTabScope
|
import com.jetpackduba.gitnuro.LocalTabScope
|
||||||
import com.jetpackduba.gitnuro.credentials.CredentialsState
|
import com.jetpackduba.gitnuro.credentials.CredentialsState
|
||||||
import com.jetpackduba.gitnuro.ui.dialogs.CloneDialog
|
import com.jetpackduba.gitnuro.ui.dialogs.*
|
||||||
import com.jetpackduba.gitnuro.ui.dialogs.PasswordDialog
|
|
||||||
import com.jetpackduba.gitnuro.ui.dialogs.UserPasswordDialog
|
|
||||||
import com.jetpackduba.gitnuro.ui.dialogs.settings.SettingsDialog
|
import com.jetpackduba.gitnuro.ui.dialogs.settings.SettingsDialog
|
||||||
import com.jetpackduba.gitnuro.viewmodels.RepositorySelectionStatus
|
import com.jetpackduba.gitnuro.viewmodels.RepositorySelectionStatus
|
||||||
import com.jetpackduba.gitnuro.viewmodels.TabViewModel
|
import com.jetpackduba.gitnuro.viewmodels.TabViewModel
|
||||||
@ -191,7 +189,7 @@ fun CredentialsDialog(gitManager: TabViewModel) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
} else if (credentialsState == CredentialsState.SshCredentialsRequested) {
|
} else if (credentialsState == CredentialsState.SshCredentialsRequested) {
|
||||||
PasswordDialog(
|
SshPasswordDialog(
|
||||||
onReject = {
|
onReject = {
|
||||||
gitManager.credentialsDenied()
|
gitManager.credentialsDenied()
|
||||||
},
|
},
|
||||||
@ -199,5 +197,14 @@ fun CredentialsDialog(gitManager: TabViewModel) {
|
|||||||
gitManager.sshCredentialsAccepted(password)
|
gitManager.sshCredentialsAccepted(password)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
} else if (credentialsState == CredentialsState.GpgCredentialsRequested) {
|
||||||
|
GpgPasswordDialog(
|
||||||
|
onReject = {
|
||||||
|
gitManager.credentialsDenied()
|
||||||
|
},
|
||||||
|
onAccept = { password ->
|
||||||
|
gitManager.gpgCredentialsAccepted(password)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package com.jetpackduba.gitnuro.ui.dialogs
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
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.focusProperties
|
||||||
|
import androidx.compose.ui.focus.focusRequester
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.input.key.onPreviewKeyEvent
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.jetpackduba.gitnuro.keybindings.KeybindingOption
|
||||||
|
import com.jetpackduba.gitnuro.keybindings.matchesBinding
|
||||||
|
import com.jetpackduba.gitnuro.theme.outlinedTextFieldColors
|
||||||
|
import com.jetpackduba.gitnuro.theme.onBackgroundSecondary
|
||||||
|
import com.jetpackduba.gitnuro.ui.components.AdjustableOutlinedTextField
|
||||||
|
import com.jetpackduba.gitnuro.ui.components.PrimaryButton
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun GpgPasswordDialog(
|
||||||
|
onReject: () -> Unit,
|
||||||
|
onAccept: (password: String) -> Unit
|
||||||
|
) {
|
||||||
|
PasswordDialog(
|
||||||
|
"Introduce your GPG key's password",
|
||||||
|
"Your GPG key is protected with a password",
|
||||||
|
"key.svg",
|
||||||
|
onReject,
|
||||||
|
onAccept,
|
||||||
|
)
|
||||||
|
}
|
@ -25,8 +25,11 @@ import com.jetpackduba.gitnuro.ui.components.PrimaryButton
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PasswordDialog(
|
fun PasswordDialog(
|
||||||
|
title: String,
|
||||||
|
subtitle: String,
|
||||||
|
icon: String,
|
||||||
onReject: () -> Unit,
|
onReject: () -> Unit,
|
||||||
onAccept: (password: String) -> Unit
|
onAccept: (password: String) -> Unit,
|
||||||
) {
|
) {
|
||||||
var passwordField by remember { mutableStateOf("") }
|
var passwordField by remember { mutableStateOf("") }
|
||||||
val passwordFieldFocusRequester = remember { FocusRequester() }
|
val passwordFieldFocusRequester = remember { FocusRequester() }
|
||||||
@ -39,7 +42,7 @@ fun PasswordDialog(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
Icon(
|
Icon(
|
||||||
painterResource("lock.svg"),
|
painterResource(icon),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(64.dp)
|
.size(64.dp)
|
||||||
@ -48,7 +51,7 @@ fun PasswordDialog(
|
|||||||
)
|
)
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = "Introduce your SSH key's password",
|
text = title,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(bottom = 8.dp),
|
.padding(bottom = 8.dp),
|
||||||
color = MaterialTheme.colors.onBackground,
|
color = MaterialTheme.colors.onBackground,
|
||||||
@ -56,7 +59,7 @@ fun PasswordDialog(
|
|||||||
)
|
)
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = "Your SSH key is protected with a password",
|
text = subtitle,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(bottom = 16.dp),
|
.padding(bottom = 16.dp),
|
||||||
color = MaterialTheme.colors.onBackgroundSecondary,
|
color = MaterialTheme.colors.onBackgroundSecondary,
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package com.jetpackduba.gitnuro.ui.dialogs
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
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.focusProperties
|
||||||
|
import androidx.compose.ui.focus.focusRequester
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.input.key.onPreviewKeyEvent
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.jetpackduba.gitnuro.keybindings.KeybindingOption
|
||||||
|
import com.jetpackduba.gitnuro.keybindings.matchesBinding
|
||||||
|
import com.jetpackduba.gitnuro.theme.outlinedTextFieldColors
|
||||||
|
import com.jetpackduba.gitnuro.theme.onBackgroundSecondary
|
||||||
|
import com.jetpackduba.gitnuro.ui.components.AdjustableOutlinedTextField
|
||||||
|
import com.jetpackduba.gitnuro.ui.components.PrimaryButton
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SshPasswordDialog(
|
||||||
|
onReject: () -> Unit,
|
||||||
|
onAccept: (password: String) -> Unit
|
||||||
|
) {
|
||||||
|
PasswordDialog(
|
||||||
|
"Introduce your SSH key's password",
|
||||||
|
"Your SSH key is protected with a password",
|
||||||
|
"lock.svg",
|
||||||
|
onReject,
|
||||||
|
onAccept,
|
||||||
|
)
|
||||||
|
}
|
@ -441,6 +441,10 @@ class TabViewModel @Inject constructor(
|
|||||||
abortRebaseUseCase(git)
|
abortRebaseUseCase(git)
|
||||||
rebaseInteractiveViewModel = null // shouldn't be necessary but just to make sure
|
rebaseInteractiveViewModel = null // shouldn't be necessary but just to make sure
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun gpgCredentialsAccepted(password: String) {
|
||||||
|
credentialsStateManager.updateState(CredentialsState.GpgCredentialsAccepted(password))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
1
src/main/resources/key.svg
Normal file
1
src/main/resources/key.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><g><rect fill="none" height="24" width="24"/></g><g><path d="M21,10h-8.35C11.83,7.67,9.61,6,7,6c-3.31,0-6,2.69-6,6s2.69,6,6,6c2.61,0,4.83-1.67,5.65-4H13l2,2l2-2l2,2l4-4.04L21,10z M7,15c-1.65,0-3-1.35-3-3c0-1.65,1.35-3,3-3s3,1.35,3,3C10,13.65,8.65,15,7,15z"/></g></svg>
|
After Width: | Height: | Size: 404 B |
Loading…
Reference in New Issue
Block a user