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}