spacegate_kernel/utils/auth/
basic.rs

1use std::{convert::Infallible, str::FromStr};
2
3use base64::Engine;
4use hyper::http::HeaderValue;
5
6use crate::{BoxError, BoxResult};
7
8use super::Authorization;
9
10#[derive(Debug, Clone)]
11pub struct Basic {
12    pub username: String,
13    pub password: Option<String>,
14}
15
16impl From<Authorization<Basic>> for Basic {
17    fn from(val: Authorization<Self>) -> Self {
18        val.0
19    }
20}
21
22impl From<Basic> for Authorization<Basic> {
23    fn from(val: Basic) -> Self {
24        Authorization(val)
25    }
26}
27
28impl TryInto<HeaderValue> for &Basic {
29    type Error = BoxError;
30    fn try_into(self) -> Result<HeaderValue, Self::Error> {
31        self.to_header()
32    }
33}
34
35impl TryFrom<HeaderValue> for Basic {
36    type Error = BoxError;
37    fn try_from(header: HeaderValue) -> Result<Self, Self::Error> {
38        if let Ok(header) = header.to_str() {
39            if let Some(base64_auth) = header.strip_prefix("Basic ") {
40                let auth = base64::engine::general_purpose::STANDARD.decode(base64_auth)?;
41                let auth = String::from_utf8(auth)?;
42                return Ok(Basic::infallible_parse(auth.as_str()));
43            } else {
44                Err("auth header value is not a basic auth value".into())
45            }
46        } else {
47            Err("auth header value is not a valid string".into())
48        }
49    }
50}
51
52impl std::fmt::Display for Basic {
53    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54        if let Some(password) = &self.password {
55            write!(f, "{}:{}", self.username, password)
56        } else {
57            write!(f, "{}", self.username)
58        }
59    }
60}
61
62impl Basic {
63    pub fn new(username: String, password: Option<String>) -> Self {
64        Self { username, password }
65    }
66    pub fn to_auth_string(&self) -> String {
67        match &self.password {
68            Some(password) => format!("{}:{}", self.username, password),
69            None => self.username.clone(),
70        }
71    }
72    /// # Errors
73    /// If the token is not a valid header value.
74    pub fn to_header(&self) -> BoxResult<HeaderValue> {
75        let auth_string = self.to_auth_string();
76        let mut header_str = String::with_capacity(auth_string.len() + 10);
77        header_str.push_str("Basic ");
78        base64::engine::general_purpose::STANDARD.encode_string(auth_string, &mut header_str);
79        let header = HeaderValue::from_maybe_shared(header_str)?;
80        Ok(header)
81    }
82    pub fn infallible_parse(auth: &str) -> Self {
83        if let Some((username, password)) = auth.split_once(':') {
84            Self::new(username.to_string(), Some(password.to_string()))
85        } else {
86            Self::new(auth.to_string(), None)
87        }
88    }
89}
90
91impl FromStr for Basic {
92    type Err = Infallible;
93
94    fn from_str(s: &str) -> Result<Self, Self::Err> {
95        Ok(Basic::infallible_parse(s))
96    }
97}