rust-lang / git2-rs

libgit2 bindings for Rust
https://docs.rs/git2
Apache License 2.0
1.72k stars 390 forks source link

authentication required but no callback set #329

Closed JMLX42 closed 6 years ago

JMLX42 commented 6 years ago

I'm using an env var to get the path to the SSH private key. This key is a passphrase-less GitLab deploy key. That key works great when I use it with a classic git clone.

But it doesn't work with RepoBuilder::clone().

Expected result

Repository is cloned.

Actual result

The following error:

authentication required but no callback set; class=Ssh (23)

How to reproduce

pub fn git_credentials_callback(
    _user: &str,
    _user_from_url: Option<&str>,
    _cred: git2::CredentialType,
) -> Result<git2::Cred, git2::Error> {
    match env::var("GPM_SSH_KEY") {
        Ok(k) => {
            debug!("authenticate with private key located in {}", k);

            git2::Cred::ssh_key("git", None, std::path::Path::new(&k), None)
        },
        _ => Err(git2::Error::from_str("unable to get private key from GPM_SSH_KEY")),
    }
}

fn get_or_init_repo(cache : &std::path::Path, remote : &String) -> Result<git2::Repository, git2::Error> {
    let data_url = match Url::parse(remote) {
        Ok(data_url) => data_url,
        Err(e) => panic!("failed to parse url: {}", e),
    };
    let path = cache.deref().join(data_url.host_str().unwrap()).join(&data_url.path()[1..]);

    if path.exists() {
        debug!("use existing repository already in cache {}", path.to_str().unwrap());
        return git2::Repository::open(path);
    }

    let mut callbacks = git2::RemoteCallbacks::new();
    callbacks.credentials(git_credentials_callback);

    let mut opts = git2::FetchOptions::new();
    opts.remote_callbacks(callbacks);
    opts.download_tags(git2::AutotagOption::All);

    let mut builder = git2::build::RepoBuilder::new();
    builder.fetch_options(opts);
    builder.branch("master");

    debug!("start cloning repository {} in {}", remote, path.to_str().unwrap());

    match builder.clone(remote, &path) {
        Ok(r) => {
            debug!("repository cloned");

            Ok(r)
        },
        Err(e) => Err(e)
    }
}
JMLX42 commented 6 years ago

It turns ou _cred contains only git2::CredentialType::USERNAME. But my git server clearly works with a SSH key when I do a git clone. Is there a way to make sure the requested credentials are git2::CredentialType::SSH_KEY?

JMLX42 commented 6 years ago

Ok got it working by reading this comment:

https://github.com/libgit2/git2go/issues/199#issuecomment-161351009

The callback is called twice:

Here is the right callback implementation:

pub fn git_credentials_callback(
    _user: &str,
    _user_from_url: Option<&str>,
    _cred: git2::CredentialType,
) -> Result<git2::Cred, git2::Error> {
    let user = _user_from_url.unwrap_or("git");

    if _cred.contains(git2::CredentialType::USERNAME) {
        return git2::Cred::username(user);
    }

    match env::var("GPM_SSH_KEY") {
        Ok(k) => {

            debug!("authenticate with user {} and private key located in {}", user, k);
            git2::Cred::ssh_key(user, None, std::path::Path::new(&k), None)
        },
        _ => Err(git2::Error::from_str("unable to get private key from GPM_SSH_KEY")),
    }
}
neithernut commented 6 years ago

Maybe it would make sense to extend the documentation for either the callback or the function accepting it.

alexcrichton commented 6 years ago

Certainly! I think the documentation around authenticated clones could definitely use a boost

darakeon commented 2 years ago

I'm using callback as:

fn git_credentials_callback(
    _url: &str,
    username: Option<&str>,
    _cred_type: CredentialType,
) -> Result<Cred, Error> {
    let public = get_key_path(Some("pub"));
    let private = get_key_path(None);

    let password = env::var("GIT_PASSWORD")
        .expect("No GIT_PASSWORD environment variable found");

    let cred = Cred::ssh_key(
        username.unwrap(),
        Some(public.as_path()),
        private.as_path(),
        Some(&password),
    );

    let result = cred.unwrap();
    println!("{:?}", _cred_type);

    return Ok(result);
}

But it hangs. Prints a bunch of the cred_type, then fails saying could not get response, Failed to retrieve list of SSH authentication methods. Tried to find a solution at google, but always find people saying this auth method should work. An issue of this was quoted here, but did not solve my problem either.

It was working before, not working for some months now. Some said it is about new way to generate ssh keys of github, but already tried old way, same response.

jinseok9338 commented 1 year ago

I know that it might be a long shot but is there a way to authenticate with personal access code?