Skip to content

Commit

Permalink
🐛 Fixed credential retrieval for SSH without username to work correct…
Browse files Browse the repository at this point in the history
…ly (#45)

* Fixed credential retrieval for SSH without username to work correctly

Fixes #44

* Retrieve SSH username from ssh_config

Fixes #42

* Comment out the debug assertion

Address the review comment: #45 (comment)

* Change the local variable name to reflect function modificatio

Address the review comment: #45 (comment)
  • Loading branch information
gifnksm committed Sep 3, 2023
1 parent f0fb8f7 commit bf39d78
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 24 deletions.
30 changes: 10 additions & 20 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ pub mod ui4dialoguer;
use std::error::Error;

pub struct CredentialHandler {
usernames: Vec<String>,
username_attempts_count: usize,
username_candidates: Vec<String>,
ssh_attempts_count: usize,
ssh_key_candidates: Vec<std::path::PathBuf>,
username_attempts_count: usize,
cred_helper_bad: Option<bool>,
cfg: git2::Config,
ui: Box<dyn CredentialUI>,
Expand All @@ -56,22 +56,11 @@ impl CredentialHandler {
}

pub fn new_with_ui(cfg: git2::Config, ui: Box<dyn CredentialUI>) -> Self {
let mut usernames = vec!["".to_string(), "git".to_string()];
if let Ok(s) = std::env::var("USER").or_else(|_| std::env::var("USERNAME")) {
usernames.push(s);
}

// let mut cred_helper = git2::CredentialHelper::new(url);
// cred_helper.config(cfg);
// if let Some(ref s) = cred_helper.username {
// usernames.push(s.clone());
// }

CredentialHandler {
usernames,
username_attempts_count: 0,
username_candidates: vec![],
ssh_attempts_count: 0,
ssh_key_candidates: vec![],
username_attempts_count: 0,
cred_helper_bad: None,
cfg,
ui,
Expand Down Expand Up @@ -137,11 +126,12 @@ impl CredentialHandler {
// debug_assert!(username.is_none());
let idx = self.username_attempts_count;
self.username_attempts_count += 1;
return match self.usernames.get(idx).map(|s| &s[..]) {
Some("") if username.is_none() => {
Err(git2::Error::from_str("gonna try usernames later"))
}
Some("") => git2::Cred::username(username.unwrap_or("")),
if idx == 0 {
let maybe_host = extract_host(url)?;
self.username_candidates =
ssh_config::find_username_candidates(maybe_host.as_deref())?;
}
return match self.username_candidates.get(idx).map(|s| &s[..]) {
Some(s) => git2::Cred::username(s),
_ => Err(git2::Error::from_str("no more username to try")),
};
Expand Down
48 changes: 44 additions & 4 deletions src/ssh_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ use std::path;
#[allow(dead_code)]
pub struct SSHConfigParser;

fn find_username_in_ssh_config(host: &str) -> Result<Option<String>, git2::Error> {
match read_ssh_config_as_string()? {
Some(content) => find_username_for_host_in_config(host, &content),
_ => Ok(None),
}
}

fn find_ssh_key_in_ssh_config(host: &str) -> Result<Option<String>, git2::Error> {
match read_ssh_config_as_string()? {
Some(content) => find_ssh_key_for_host_in_config(host, &content),
Expand Down Expand Up @@ -49,9 +56,24 @@ fn read_ssh_config_as_string() -> Result<Option<String>, git2::Error> {
.unwrap_or(Ok(None))
}

fn find_username_for_host_in_config(
host: &str,
ssh_config_str: &str,
) -> Result<Option<String>, git2::Error> {
find_entry_for_host_in_config(host, ssh_config_str, "User")
}

fn find_ssh_key_for_host_in_config(
host: &str,
ssh_config_str: &str,
) -> Result<Option<String>, git2::Error> {
find_entry_for_host_in_config(host, ssh_config_str, "IdentityFile")
}

fn find_entry_for_host_in_config(
host: &str,
ssh_config_str: &str,
name: &str,
) -> Result<Option<String>, git2::Error> {
// trace!("parsing {:?} to find host {}", ssh_config_path, host);

Expand Down Expand Up @@ -106,11 +128,11 @@ fn find_ssh_key_for_host_in_config(
))
})?;

if key.as_str().eq_ignore_ascii_case("IdentityFile") {
let path = value.as_str().to_string();
if key.as_str().eq_ignore_ascii_case(name) {
let found_value = value.as_str().to_string();

// trace!("found IdentityFile option with value {:?}", path);
return Ok(Some(path));
// trace!("found IdentityFile option with value {:?}", found_value);
return Ok(Some(found_value));
}
}
}
Expand All @@ -120,6 +142,24 @@ fn find_ssh_key_for_host_in_config(
Ok(None)
}

pub(crate) fn find_username_candidates(host: Option<&str>) -> Result<Vec<String>, git2::Error> {
// candidates in the same order than the list from IdentityFile in ssh_config man page.
let mut candidates = vec![];
// first the candidates from .ssh/config for the target host
if let Some(host) = host {
if let Some(username_host) = find_username_in_ssh_config(host)? {
candidates.push(username_host);
}
}
// push default candidates
candidates.push("git".to_string());
if let Ok(s) = std::env::var("USER").or_else(|_| std::env::var("USERNAME")) {
candidates.push(s);
}

Ok(candidates)
}

pub(crate) fn find_ssh_key_candidates(
host: Option<&str>,
) -> Result<Vec<path::PathBuf>, git2::Error> {
Expand Down

0 comments on commit bf39d78

Please sign in to comment.