devrc/
auth.rs

1use std::{convert::TryFrom, fs};
2
3use netrc_rs::{Machine, Netrc};
4
5use crate::{
6    errors::DevrcError,
7    netrc::get_user_defined_netrc_path,
8    raw::auth::{NetrcAuth, NetrcAuthHeader},
9};
10
11pub const HEADER_AUTHORIZATION: &str = "Authorization";
12
13#[derive(Debug, Clone, Default)]
14pub enum AuthType {
15    #[default]
16    None,
17    Bearer,
18    BasicAuth,
19    Header(String),
20}
21
22#[derive(Debug, Clone, Default)]
23pub enum Auth {
24    #[default]
25    Empty,
26    Loaded {
27        machine: Machine,
28        auth_type: AuthType,
29    },
30}
31
32impl TryFrom<crate::raw::auth::Auth> for Auth {
33    type Error = DevrcError;
34
35    fn try_from(value: crate::raw::auth::Auth) -> Result<Self, Self::Error> {
36        let file = get_user_defined_netrc_path().ok_or(DevrcError::NetrcNotFound)?;
37        let content = fs::read_to_string(file).map_err(DevrcError::IoError)?;
38        let netrc = Netrc::parse(content, false).map_err(DevrcError::NetrcParsingError)?;
39
40        match value {
41            crate::raw::auth::Auth::Empty => Ok(Auth::Empty),
42            crate::raw::auth::Auth::NetrcAuth(NetrcAuth {
43                host,
44                login,
45                auth_type,
46            }) => match get_machine(&netrc, &host, &login) {
47                Some(machine) => Ok(Auth::Loaded {
48                    machine,
49                    auth_type: auth_type.into(),
50                }),
51                None => Ok(Auth::Empty),
52            },
53            crate::raw::auth::Auth::NetrcAuthHeader(NetrcAuthHeader {
54                host,
55                login,
56                header,
57            }) => match get_machine(&netrc, &host, &login) {
58                Some(machine) => Ok(Auth::Loaded {
59                    machine,
60                    auth_type: AuthType::Header(header),
61                }),
62                None => Ok(Auth::Empty),
63            },
64        }
65    }
66}
67
68impl From<crate::raw::auth::AuthType> for AuthType {
69    fn from(value: crate::raw::auth::AuthType) -> Self {
70        match value {
71            crate::raw::auth::AuthType::Empty => AuthType::None,
72            crate::raw::auth::AuthType::Bearer => AuthType::Bearer,
73            crate::raw::auth::AuthType::BasicAuth => AuthType::BasicAuth,
74        }
75    }
76}
77
78fn get_machine(netrc: &Netrc, host: &str, login: &str) -> Option<Machine> {
79    let mut default: Option<Machine> = None;
80
81    for machine in &netrc.machines {
82        match (machine.name.as_ref(), machine.login.as_ref()) {
83            (Some(record_host), Some(record_login))
84                if record_host.as_str() == host && record_login.as_str() == login =>
85            {
86                return Some(machine.clone())
87            }
88            (None, Some(record_login)) if record_login.as_str() == login => {
89                default = Some(machine.clone())
90            }
91            (_, _) => {}
92        }
93    }
94
95    default
96}
97
98impl Auth {
99    pub fn get_header(&self) -> Option<(String, String)> {
100        if let Auth::Loaded { machine, auth_type } = self {
101            let password = match &machine.password {
102                Some(password) => password,
103                None => return None,
104            };
105            let login = match &machine.login {
106                Some(login) => login,
107                None => return None,
108            };
109
110            return match auth_type {
111                AuthType::Bearer => Some((
112                    HEADER_AUTHORIZATION.to_string(),
113                    format!("Bearer {}", password),
114                )),
115                AuthType::BasicAuth => {
116                    let creds = format!("{:}:{:}", login, password);
117                    let b64 =
118                        base64::Engine::encode(&base64::engine::general_purpose::STANDARD, creds);
119                    Some((HEADER_AUTHORIZATION.to_string(), format!("Basic {}", b64)))
120                }
121                AuthType::Header(header) => Some((header.to_owned(), password.to_owned())),
122                AuthType::None => None,
123            };
124        }
125        None
126    }
127}