Skip to main content

rskit_git/embedded/
auth.rs

1//! `git2` authentication helpers.
2
3use crate::auth::TransportAuth;
4use crate::error::GitError;
5use rskit_errors::AppResult;
6
7/// Builds remote callbacks from the configured transport auth.
8#[allow(dead_code)]
9pub fn remote_callbacks(auth: Option<&TransportAuth>) -> AppResult<git2::RemoteCallbacks<'static>> {
10    let mut callbacks = git2::RemoteCallbacks::new();
11    match auth.cloned().unwrap_or_default() {
12        TransportAuth::Default => {}
13        TransportAuth::UsernamePassword { username, password } => {
14            callbacks
15                .credentials(move |_, _, _| git2::Cred::userpass_plaintext(&username, &password));
16        }
17        TransportAuth::Token { username, token } => {
18            let username = username.unwrap_or_else(|| "git".to_string());
19            callbacks.credentials(move |_, _, _| git2::Cred::userpass_plaintext(&username, &token));
20        }
21        TransportAuth::SshKey {
22            username,
23            public_key,
24            private_key,
25            passphrase,
26        } => {
27            callbacks.credentials(move |_, _, _| {
28                git2::Cred::ssh_key(
29                    &username,
30                    public_key.as_deref(),
31                    &private_key,
32                    passphrase.as_deref(),
33                )
34            });
35        }
36        TransportAuth::SshAgent { username } => {
37            callbacks.credentials(move |_, _, _| git2::Cred::ssh_key_from_agent(&username));
38        }
39    }
40    Ok(callbacks)
41}
42
43/// Validates that the requested transport auth can be expressed for `git2`.
44#[allow(dead_code)]
45pub fn validate_transport(auth: &TransportAuth) -> AppResult<()> {
46    match auth {
47        TransportAuth::Default
48        | TransportAuth::UsernamePassword { .. }
49        | TransportAuth::Token { .. }
50        | TransportAuth::SshKey { .. }
51        | TransportAuth::SshAgent { .. } => Ok(()),
52        #[allow(unreachable_patterns)]
53        _ => Err(GitError::InvalidTransport {
54            kind: "unsupported transport auth".to_string(),
55        }
56        .into()),
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use std::path::PathBuf;
63
64    use super::*;
65
66    #[test]
67    fn validates_all_supported_transport_auth_variants() {
68        let variants = [
69            TransportAuth::Default,
70            TransportAuth::UsernamePassword {
71                username: "user".to_string(),
72                password: "password".to_string(),
73            },
74            TransportAuth::Token {
75                username: None,
76                token: "token".to_string(),
77            },
78            TransportAuth::SshKey {
79                username: "git".to_string(),
80                public_key: None,
81                private_key: PathBuf::from("id_ed25519"),
82                passphrase: Some("passphrase".to_string()),
83            },
84            TransportAuth::SshAgent {
85                username: "git".to_string(),
86            },
87        ];
88
89        for auth in variants {
90            validate_transport(&auth).expect("supported auth variant validates");
91            let _callbacks =
92                remote_callbacks(Some(&auth)).expect("supported auth variant builds callbacks");
93        }
94    }
95
96    #[test]
97    fn remote_callbacks_defaults_when_auth_is_absent() {
98        let _callbacks = remote_callbacks(None).expect("default callbacks build");
99    }
100}