Skip to main content

torii_lib/util/
ssh.rs

1use std::path::PathBuf;
2use std::fs;
3use crate::error::Result;
4use dirs;
5
6/// SSH key detection and management
7pub struct SshHelper;
8
9impl SshHelper {
10    /// Check if user has SSH keys configured
11    pub fn has_ssh_keys() -> bool {
12        let ssh_dir = Self::ssh_dir();
13        if !ssh_dir.exists() {
14            return false;
15        }
16
17        // Check for common SSH key files
18        let key_files = vec![
19            "id_rsa",
20            "id_ed25519",
21            "id_ecdsa",
22            "id_dsa",
23        ];
24
25        key_files.iter().any(|key| {
26            ssh_dir.join(key).exists()
27        })
28    }
29
30    /// Get SSH directory path
31    pub fn ssh_dir() -> PathBuf {
32        dirs::home_dir()
33            .unwrap_or_else(|| PathBuf::from("."))
34            .join(".ssh")
35    }
36
37    /// List available SSH keys
38    pub fn list_keys() -> Vec<String> {
39        let ssh_dir = Self::ssh_dir();
40        if !ssh_dir.exists() {
41            return vec![];
42        }
43
44        let key_files = vec![
45            "id_rsa",
46            "id_ed25519",
47            "id_ecdsa",
48            "id_dsa",
49        ];
50
51        key_files
52            .iter()
53            .filter(|key| ssh_dir.join(key).exists())
54            .map(|s| s.to_string())
55            .collect()
56    }
57
58    /// Get public key content for display
59    #[allow(dead_code)]
60    pub fn get_public_key(key_name: &str) -> Result<String> {
61        let ssh_dir = Self::ssh_dir();
62        let pub_key_path = ssh_dir.join(format!("{}.pub", key_name));
63        
64        if !pub_key_path.exists() {
65            return Ok(String::new());
66        }
67
68        Ok(fs::read_to_string(pub_key_path)?)
69    }
70
71    /// Recommend protocol based on SSH availability
72    #[allow(dead_code)]
73    pub fn recommend_protocol() -> &'static str {
74        if Self::has_ssh_keys() {
75            "ssh"
76        } else {
77            "https"
78        }
79    }
80
81    /// Get setup instructions for SSH
82    #[allow(dead_code)]
83    pub fn get_setup_instructions() -> String {
84        let mut instructions = String::new();
85        
86        instructions.push_str("📚 SSH Setup Instructions:\n\n");
87        instructions.push_str("1. Generate SSH key:\n");
88        instructions.push_str("   ssh-keygen -t ed25519 -C \"your_email@example.com\"\n\n");
89        instructions.push_str("2. Start SSH agent:\n");
90        instructions.push_str("   eval \"$(ssh-agent -s)\"\n");
91        instructions.push_str("   ssh-add ~/.ssh/id_ed25519\n\n");
92        instructions.push_str("3. Copy public key:\n");
93        instructions.push_str("   cat ~/.ssh/id_ed25519.pub\n\n");
94        instructions.push_str("4. Add to your platform:\n");
95        instructions.push_str("   • GitHub: https://github.com/settings/keys\n");
96        instructions.push_str("   • GitLab: https://gitlab.com/-/profile/keys\n");
97        instructions.push_str("   • Bitbucket: https://bitbucket.org/account/settings/ssh-keys/\n");
98        
99        instructions
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_ssh_dir() {
109        let dir = SshHelper::ssh_dir();
110        assert!(dir.to_string_lossy().contains(".ssh"));
111    }
112
113    #[test]
114    fn test_recommend_protocol() {
115        let protocol = SshHelper::recommend_protocol();
116        assert!(protocol == "ssh" || protocol == "https");
117    }
118}