Skip to main content

git_same/auth/
ssh.rs

1//! SSH key detection for git operations.
2//!
3//! Note: SSH keys authenticate git clone/fetch/pull operations,
4//! NOT GitHub API calls. This module detects if SSH keys are configured
5//! so we can provide better error messages and suggest SSH clone URLs.
6
7use std::path::PathBuf;
8use std::process::Command;
9
10/// Check if SSH is likely configured for GitHub.
11///
12/// Uses BatchMode to avoid interactive prompts. If the host key is not
13/// already known, this returns false (user should run `ssh -T git@github.com`
14/// manually to verify and accept the host key).
15pub fn has_github_ssh_access() -> bool {
16    // Try to test SSH connection to GitHub
17    // BatchMode=yes prevents interactive prompts (for host key verification, passwords, etc.)
18    // ConnectTimeout=5 prevents hanging on network issues
19    let output = Command::new("ssh")
20        .args([
21            "-T",
22            "-o",
23            "BatchMode=yes",
24            "-o",
25            "ConnectTimeout=5",
26            "git@github.com",
27        ])
28        .output();
29
30    if let Ok(output) = output {
31        // GitHub SSH test returns exit code 1 with success message
32        // "Hi username! You've successfully authenticated..."
33        let stderr = String::from_utf8_lossy(&output.stderr);
34        stderr.contains("successfully authenticated")
35    } else {
36        false
37    }
38}
39
40/// Detect if SSH keys exist in the standard locations.
41pub fn has_ssh_keys() -> bool {
42    let home = match std::env::var("HOME") {
43        Ok(h) => h,
44        Err(_) => return false,
45    };
46
47    let ssh_dir = PathBuf::from(home).join(".ssh");
48
49    // Check for common SSH key types
50    let key_files = [
51        "id_rsa",
52        "id_ed25519",
53        "id_ecdsa",
54        "id_dsa",
55        "github_rsa",
56        "github_ed25519",
57    ];
58
59    for key_file in &key_files {
60        let key_path = ssh_dir.join(key_file);
61        if key_path.exists() {
62            return true;
63        }
64    }
65
66    false
67}
68
69/// Get SSH key files that exist.
70pub fn get_ssh_key_files() -> Vec<PathBuf> {
71    let home = match std::env::var("HOME") {
72        Ok(h) => h,
73        Err(_) => return vec![],
74    };
75
76    let ssh_dir = PathBuf::from(home).join(".ssh");
77
78    let key_files = [
79        "id_rsa",
80        "id_ed25519",
81        "id_ecdsa",
82        "id_dsa",
83        "github_rsa",
84        "github_ed25519",
85    ];
86
87    key_files
88        .iter()
89        .map(|f| ssh_dir.join(f))
90        .filter(|p| p.exists())
91        .collect()
92}
93
94/// Check if SSH agent is running.
95pub fn has_ssh_agent() -> bool {
96    std::env::var("SSH_AUTH_SOCK").is_ok()
97}
98
99#[cfg(test)]
100#[path = "ssh_tests.rs"]
101mod tests;