mirror of
https://github.com/Gu1llaum-3/sshm.git
synced 2026-01-27 03:04:21 +01:00
fix: SSH identity file paths with spaces and edit form navigation
- Quote IdentityFile paths containing spaces to prevent SSH config errors - Fix edit form ESC/Ctrl+C to return to main view instead of quitting - Improve edit form navigation consistency with add form - Fix focus management when adding host fields with Ctrl+A
This commit is contained in:
@@ -418,6 +418,20 @@ func getMainConfigPath() string {
|
||||
return absPath
|
||||
}
|
||||
|
||||
// formatSSHConfigValue formats a value for SSH config file, adding quotes if necessary
|
||||
func formatSSHConfigValue(value string) string {
|
||||
if value == "" {
|
||||
return value
|
||||
}
|
||||
|
||||
// If the value contains spaces, wrap it in quotes
|
||||
if strings.Contains(value, " ") {
|
||||
return `"` + value + `"`
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
// AddSSHHost adds a new SSH host to the config file
|
||||
func AddSSHHost(host SSHHost) error {
|
||||
configPath, err := GetDefaultSSHConfigPath()
|
||||
@@ -495,7 +509,7 @@ func AddSSHHostToFile(host SSHHost, configPath string) error {
|
||||
}
|
||||
|
||||
if host.Identity != "" {
|
||||
_, err = file.WriteString(fmt.Sprintf(" IdentityFile %s\n", host.Identity))
|
||||
_, err = file.WriteString(fmt.Sprintf(" IdentityFile %s\n", formatSSHConfigValue(host.Identity)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -795,7 +809,7 @@ func UpdateSSHHostInFile(oldName string, newHost SSHHost, configPath string) err
|
||||
newLines = append(newLines, " Port "+newHost.Port)
|
||||
}
|
||||
if newHost.Identity != "" {
|
||||
newLines = append(newLines, " IdentityFile "+newHost.Identity)
|
||||
newLines = append(newLines, " IdentityFile "+formatSSHConfigValue(newHost.Identity))
|
||||
}
|
||||
if newHost.ProxyJump != "" {
|
||||
newLines = append(newLines, " ProxyJump "+newHost.ProxyJump)
|
||||
@@ -843,7 +857,7 @@ func UpdateSSHHostInFile(oldName string, newHost SSHHost, configPath string) err
|
||||
newLines = append(newLines, " Port "+newHost.Port)
|
||||
}
|
||||
if newHost.Identity != "" {
|
||||
newLines = append(newLines, " IdentityFile "+newHost.Identity)
|
||||
newLines = append(newLines, " IdentityFile "+formatSSHConfigValue(newHost.Identity))
|
||||
}
|
||||
if newHost.ProxyJump != "" {
|
||||
newLines = append(newLines, " ProxyJump "+newHost.ProxyJump)
|
||||
@@ -927,7 +941,7 @@ func UpdateSSHHostInFile(oldName string, newHost SSHHost, configPath string) err
|
||||
newLines = append(newLines, " Port "+newHost.Port)
|
||||
}
|
||||
if newHost.Identity != "" {
|
||||
newLines = append(newLines, " IdentityFile "+newHost.Identity)
|
||||
newLines = append(newLines, " IdentityFile "+formatSSHConfigValue(newHost.Identity))
|
||||
}
|
||||
if newHost.ProxyJump != "" {
|
||||
newLines = append(newLines, " ProxyJump "+newHost.ProxyJump)
|
||||
@@ -975,7 +989,7 @@ func UpdateSSHHostInFile(oldName string, newHost SSHHost, configPath string) err
|
||||
newLines = append(newLines, " Port "+newHost.Port)
|
||||
}
|
||||
if newHost.Identity != "" {
|
||||
newLines = append(newLines, " IdentityFile "+newHost.Identity)
|
||||
newLines = append(newLines, " IdentityFile "+formatSSHConfigValue(newHost.Identity))
|
||||
}
|
||||
if newHost.ProxyJump != "" {
|
||||
newLines = append(newLines, " ProxyJump "+newHost.ProxyJump)
|
||||
@@ -1469,7 +1483,7 @@ func UpdateMultiHostBlock(originalHosts, newHosts []string, commonProperties SSH
|
||||
newLines = append(newLines, " Port "+commonProperties.Port)
|
||||
}
|
||||
if commonProperties.Identity != "" {
|
||||
newLines = append(newLines, " IdentityFile "+commonProperties.Identity)
|
||||
newLines = append(newLines, " IdentityFile "+formatSSHConfigValue(commonProperties.Identity))
|
||||
}
|
||||
if commonProperties.ProxyJump != "" {
|
||||
newLines = append(newLines, " ProxyJump "+commonProperties.ProxyJump)
|
||||
@@ -1549,7 +1563,7 @@ func UpdateMultiHostBlock(originalHosts, newHosts []string, commonProperties SSH
|
||||
newLines = append(newLines, " Port "+commonProperties.Port)
|
||||
}
|
||||
if commonProperties.Identity != "" {
|
||||
newLines = append(newLines, " IdentityFile "+commonProperties.Identity)
|
||||
newLines = append(newLines, " IdentityFile "+formatSSHConfigValue(commonProperties.Identity))
|
||||
}
|
||||
if commonProperties.ProxyJump != "" {
|
||||
newLines = append(newLines, " ProxyJump "+commonProperties.ProxyJump)
|
||||
|
||||
@@ -1442,3 +1442,109 @@ func contains(slice []string, item string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Helper function to create temporary config files for testing
|
||||
func createTempConfigFile(content string) (string, error) {
|
||||
tempFile, err := os.CreateTemp("", "ssh_config_test_*.conf")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer tempFile.Close()
|
||||
|
||||
_, err = tempFile.WriteString(content)
|
||||
if err != nil {
|
||||
os.Remove(tempFile.Name())
|
||||
return "", err
|
||||
}
|
||||
|
||||
return tempFile.Name(), nil
|
||||
}
|
||||
|
||||
func TestFormatSSHConfigValue(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "simple path without spaces",
|
||||
input: "/home/user/.ssh/id_rsa",
|
||||
expected: "/home/user/.ssh/id_rsa",
|
||||
},
|
||||
{
|
||||
name: "path with spaces",
|
||||
input: "/home/user/My Documents/ssh key",
|
||||
expected: "\"/home/user/My Documents/ssh key\"",
|
||||
},
|
||||
{
|
||||
name: "Windows path with spaces",
|
||||
input: `G:\My Drive\7 - Tech\9 - SSH Keys\Server_WF.opk`,
|
||||
expected: `"G:\My Drive\7 - Tech\9 - SSH Keys\Server_WF.opk"`,
|
||||
},
|
||||
{
|
||||
name: "path with quotes but no spaces",
|
||||
input: `/home/user/key"with"quotes`,
|
||||
expected: `/home/user/key"with"quotes`,
|
||||
},
|
||||
{
|
||||
name: "path with spaces and quotes",
|
||||
input: `/home/user/key "with" quotes`,
|
||||
expected: `"/home/user/key "with" quotes"`,
|
||||
},
|
||||
{
|
||||
name: "empty path",
|
||||
input: "",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "path with single space at end",
|
||||
input: "/home/user/key ",
|
||||
expected: "\"/home/user/key \"",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := formatSSHConfigValue(tt.input)
|
||||
if result != tt.expected {
|
||||
t.Errorf("formatSSHConfigValue(%q) = %q, want %q", tt.input, result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddSSHHostWithSpacesInPath(t *testing.T) {
|
||||
// Create temporary config file
|
||||
configFile, err := createTempConfigFile(`Host existing
|
||||
HostName existing.com
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create config file: %v", err)
|
||||
}
|
||||
defer os.Remove(configFile)
|
||||
|
||||
// Test adding host with path containing spaces
|
||||
host := SSHHost{
|
||||
Name: "test-spaces",
|
||||
Hostname: "test.com",
|
||||
User: "testuser",
|
||||
Identity: "/path/with spaces/key file",
|
||||
}
|
||||
|
||||
err = AddSSHHostToFile(host, configFile)
|
||||
if err != nil {
|
||||
t.Fatalf("AddSSHHostToFile failed: %v", err)
|
||||
}
|
||||
|
||||
// Read the file and verify quotes are added
|
||||
content, err := os.ReadFile(configFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read config file: %v", err)
|
||||
}
|
||||
|
||||
contentStr := string(content)
|
||||
expectedIdentityLine := ` IdentityFile "/path/with spaces/key file"`
|
||||
if !strings.Contains(contentStr, expectedIdentityLine) {
|
||||
t.Errorf("Expected identity file line with quotes not found.\nContent:\n%s\nExpected line: %s", contentStr, expectedIdentityLine)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user