http_types/auth/
basic_auth.rs1use crate::auth::{AuthenticationScheme, Authorization};
2use crate::headers::{HeaderName, HeaderValue, Headers, AUTHORIZATION};
3use crate::Status;
4use crate::{bail_status as bail, ensure_status as ensure};
5
6#[derive(Debug)]
35pub struct BasicAuth {
36 username: String,
37 password: String,
38}
39
40impl BasicAuth {
41 pub fn new<U, P>(username: U, password: P) -> Self
43 where
44 U: AsRef<str>,
45 P: AsRef<str>,
46 {
47 let username = username.as_ref().to_owned();
48 let password = password.as_ref().to_owned();
49 Self { username, password }
50 }
51
52 pub fn from_headers(headers: impl AsRef<Headers>) -> crate::Result<Option<Self>> {
54 let auth = match Authorization::from_headers(headers)? {
55 Some(auth) => auth,
56 None => return Ok(None),
57 };
58
59 let scheme = auth.scheme();
60 ensure!(
61 matches!(scheme, AuthenticationScheme::Basic),
62 400,
63 "Expected basic auth scheme found `{}`",
64 scheme
65 );
66 Self::from_credentials(auth.credentials()).map(Some)
67 }
68
69 pub fn from_credentials(credentials: impl AsRef<[u8]>) -> crate::Result<Self> {
71 let bytes = base64::decode(credentials).status(400)?;
72 let credentials = String::from_utf8(bytes).status(400)?;
73
74 let mut iter = credentials.splitn(2, ':');
75 let username = iter.next();
76 let password = iter.next();
77
78 let (username, password) = match (username, password) {
79 (Some(username), Some(password)) => (username.to_string(), password.to_string()),
80 (Some(_), None) => bail!(400, "Expected basic auth to contain a password"),
81 (None, _) => bail!(400, "Expected basic auth to contain a username"),
82 };
83
84 Ok(Self { username, password })
85 }
86
87 pub fn apply(&self, mut headers: impl AsMut<Headers>) {
89 headers.as_mut().insert(self.name(), self.value());
90 }
91
92 pub fn name(&self) -> HeaderName {
94 AUTHORIZATION
95 }
96
97 pub fn value(&self) -> HeaderValue {
99 let scheme = AuthenticationScheme::Basic;
100 let credentials = base64::encode(format!("{}:{}", self.username, self.password));
101 let auth = Authorization::new(scheme, credentials);
102 auth.value()
103 }
104
105 pub fn username(&self) -> &str {
107 self.username.as_str()
108 }
109
110 pub fn password(&self) -> &str {
112 self.password.as_str()
113 }
114}
115
116#[cfg(test)]
117mod test {
118 use super::*;
119 use crate::headers::Headers;
120
121 #[test]
122 fn smoke() -> crate::Result<()> {
123 let username = "nori";
124 let password = "secret_fish!!";
125 let authz = BasicAuth::new(username, password);
126
127 let mut headers = Headers::new();
128 authz.apply(&mut headers);
129
130 let authz = BasicAuth::from_headers(headers)?.unwrap();
131
132 assert_eq!(authz.username(), username);
133 assert_eq!(authz.password(), password);
134 Ok(())
135 }
136
137 #[test]
138 fn bad_request_on_parse_error() {
139 let mut headers = Headers::new();
140 headers.insert(AUTHORIZATION, "<nori ate the tag. yum.>");
141 let err = BasicAuth::from_headers(headers).unwrap_err();
142 assert_eq!(err.status(), 400);
143 }
144}