mirror of
https://github.com/TermoraDev/termora.git
synced 2026-01-16 02:12:58 +08:00
feat: supports one-time authorised connection (#211)
This commit is contained in:
133
src/main/kotlin/app/termora/RequestAuthenticationDialog.kt
Normal file
133
src/main/kotlin/app/termora/RequestAuthenticationDialog.kt
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
package app.termora
|
||||||
|
|
||||||
|
import app.termora.keymgr.KeyManager
|
||||||
|
import app.termora.keymgr.OhKeyPair
|
||||||
|
import com.formdev.flatlaf.extras.components.FlatComboBox
|
||||||
|
import com.jgoodies.forms.builder.FormBuilder
|
||||||
|
import com.jgoodies.forms.layout.FormLayout
|
||||||
|
import java.awt.BorderLayout
|
||||||
|
import java.awt.Component
|
||||||
|
import java.awt.Dimension
|
||||||
|
import java.awt.Window
|
||||||
|
import java.awt.event.ItemEvent
|
||||||
|
import javax.swing.*
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
|
class RequestAuthenticationDialog(owner: Window) : DialogWrapper(owner) {
|
||||||
|
|
||||||
|
private val authenticationTypeComboBox = FlatComboBox<AuthenticationType>()
|
||||||
|
private val rememberCheckBox = JCheckBox("Remember")
|
||||||
|
private val passwordPanel = JPanel(BorderLayout())
|
||||||
|
private val passwordPasswordField = OutlinePasswordField()
|
||||||
|
private val publicKeyComboBox = FlatComboBox<OhKeyPair>()
|
||||||
|
private val keyManager get() = KeyManager.getInstance()
|
||||||
|
private var authentication = Authentication.No
|
||||||
|
|
||||||
|
init {
|
||||||
|
isModal = true
|
||||||
|
title = "SSH User Authentication"
|
||||||
|
controlsVisible = false
|
||||||
|
|
||||||
|
init()
|
||||||
|
|
||||||
|
pack()
|
||||||
|
|
||||||
|
size = Dimension(max(380, size.width), size.height)
|
||||||
|
|
||||||
|
setLocationRelativeTo(null)
|
||||||
|
|
||||||
|
publicKeyComboBox.renderer = object : DefaultListCellRenderer() {
|
||||||
|
override fun getListCellRendererComponent(
|
||||||
|
list: JList<*>?,
|
||||||
|
value: Any?,
|
||||||
|
index: Int,
|
||||||
|
isSelected: Boolean,
|
||||||
|
cellHasFocus: Boolean
|
||||||
|
): Component {
|
||||||
|
return super.getListCellRendererComponent(
|
||||||
|
list,
|
||||||
|
if (value is OhKeyPair) value.name else value,
|
||||||
|
index,
|
||||||
|
isSelected,
|
||||||
|
cellHasFocus
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (keyPair in keyManager.getOhKeyPairs()) {
|
||||||
|
publicKeyComboBox.addItem(keyPair)
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticationTypeComboBox.addItemListener {
|
||||||
|
if (it.stateChange == ItemEvent.SELECTED) {
|
||||||
|
switchPasswordComponent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createCenterPanel(): JComponent {
|
||||||
|
authenticationTypeComboBox.addItem(AuthenticationType.Password)
|
||||||
|
authenticationTypeComboBox.addItem(AuthenticationType.PublicKey)
|
||||||
|
val formMargin = "7dlu"
|
||||||
|
val layout = FormLayout(
|
||||||
|
"left:pref, $formMargin, default:grow",
|
||||||
|
"pref, $formMargin, pref"
|
||||||
|
)
|
||||||
|
|
||||||
|
switchPasswordComponent()
|
||||||
|
|
||||||
|
return FormBuilder.create().padding("$formMargin, $formMargin, $formMargin, $formMargin")
|
||||||
|
.layout(layout)
|
||||||
|
.add("${I18n.getString("termora.new-host.general.authentication")}:").xy(1, 1)
|
||||||
|
.add(authenticationTypeComboBox).xy(3, 1)
|
||||||
|
.add("${I18n.getString("termora.new-host.general.password")}:").xy(1, 3)
|
||||||
|
.add(passwordPanel).xy(3, 3)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun switchPasswordComponent() {
|
||||||
|
passwordPanel.removeAll()
|
||||||
|
if (authenticationTypeComboBox.selectedItem == AuthenticationType.Password) {
|
||||||
|
passwordPanel.add(passwordPasswordField, BorderLayout.CENTER)
|
||||||
|
} else if (authenticationTypeComboBox.selectedItem == AuthenticationType.PublicKey) {
|
||||||
|
passwordPanel.add(publicKeyComboBox, BorderLayout.CENTER)
|
||||||
|
}
|
||||||
|
passwordPanel.revalidate()
|
||||||
|
passwordPanel.repaint()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createSouthPanel(): JComponent? {
|
||||||
|
val box = super.createSouthPanel() ?: return null
|
||||||
|
rememberCheckBox.isFocusable = false
|
||||||
|
box.add(rememberCheckBox, 0)
|
||||||
|
return box
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun doCancelAction() {
|
||||||
|
authentication = Authentication.No
|
||||||
|
super.doCancelAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun doOKAction() {
|
||||||
|
val type = authenticationTypeComboBox.selectedItem as AuthenticationType
|
||||||
|
authentication = authentication.copy(
|
||||||
|
type = type,
|
||||||
|
password = if (type == AuthenticationType.Password) String(passwordPasswordField.password)
|
||||||
|
else (publicKeyComboBox.selectedItem as OhKeyPair).id
|
||||||
|
)
|
||||||
|
super.doOKAction()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAuthentication(): Authentication {
|
||||||
|
isModal = true
|
||||||
|
SwingUtilities.invokeLater { passwordPasswordField.requestFocusInWindow() }
|
||||||
|
isVisible = true
|
||||||
|
return authentication
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isRemembered(): Boolean {
|
||||||
|
return rememberCheckBox.isSelected
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -87,12 +87,25 @@ class SSHTerminalTab(windowScope: WindowScope, host: Host) :
|
|||||||
terminal.write("SSH client is opening...\r\n")
|
terminal.write("SSH client is opening...\r\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var host = this.host.copy(authentication = this.host.authentication.copy())
|
||||||
val owner = SwingUtilities.getWindowAncestor(terminalPanel)
|
val owner = SwingUtilities.getWindowAncestor(terminalPanel)
|
||||||
val client = SshClients.openClient(host).also { sshClient = it }
|
val client = SshClients.openClient(host).also { sshClient = it }
|
||||||
client.serverKeyVerifier = DialogServerKeyVerifier(owner)
|
client.serverKeyVerifier = DialogServerKeyVerifier(owner)
|
||||||
// keyboard interactive
|
// keyboard interactive
|
||||||
client.userInteraction = TerminalUserInteraction(owner)
|
client.userInteraction = TerminalUserInteraction(owner)
|
||||||
|
|
||||||
|
if (host.authentication.type == AuthenticationType.No) {
|
||||||
|
withContext(Dispatchers.Swing) {
|
||||||
|
val dialog = RequestAuthenticationDialog(owner)
|
||||||
|
val authentication = dialog.getAuthentication()
|
||||||
|
host = host.copy(authentication = authentication)
|
||||||
|
// save
|
||||||
|
if (dialog.isRemembered()) {
|
||||||
|
HostManager.getInstance().addHost(this@SSHTerminalTab.host.copy(authentication = authentication))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val sessionListener = MySessionListener()
|
val sessionListener = MySessionListener()
|
||||||
val channelListener = MyChannelListener()
|
val channelListener = MyChannelListener()
|
||||||
|
|
||||||
|
|||||||
@@ -104,7 +104,8 @@ class SftpFileSystemPanel(
|
|||||||
|
|
||||||
private suspend fun doConnect() {
|
private suspend fun doConnect() {
|
||||||
|
|
||||||
val host = this.host ?: return
|
val thisHost = this.host ?: return
|
||||||
|
var host = thisHost.copy(authentication = thisHost.authentication.copy())
|
||||||
|
|
||||||
closeIO()
|
closeIO()
|
||||||
|
|
||||||
@@ -114,6 +115,17 @@ class SftpFileSystemPanel(
|
|||||||
val owner = SwingUtilities.getWindowAncestor(this@SftpFileSystemPanel)
|
val owner = SwingUtilities.getWindowAncestor(this@SftpFileSystemPanel)
|
||||||
client.userInteraction = TerminalUserInteraction(owner)
|
client.userInteraction = TerminalUserInteraction(owner)
|
||||||
client.serverKeyVerifier = DialogServerKeyVerifier(owner)
|
client.serverKeyVerifier = DialogServerKeyVerifier(owner)
|
||||||
|
// 弹出授权框
|
||||||
|
if (host.authentication.type == AuthenticationType.No) {
|
||||||
|
val dialog = RequestAuthenticationDialog(owner)
|
||||||
|
val authentication = dialog.getAuthentication()
|
||||||
|
host = host.copy(authentication = authentication)
|
||||||
|
// save
|
||||||
|
if (dialog.isRemembered()) {
|
||||||
|
HostManager.getInstance()
|
||||||
|
.addHost(host.copy(authentication = authentication))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val session = SshClients.openSession(host, client).apply { session = this }
|
val session = SshClients.openSession(host, client).apply { session = this }
|
||||||
fileSystem = SftpClientFactory.instance().createSftpFileSystem(session)
|
fileSystem = SftpClientFactory.instance().createSftpFileSystem(session)
|
||||||
|
|||||||
Reference in New Issue
Block a user