1pub struct Response {
5 pub headers: Vec<(&'static str, String)>,
6 pub status_code: u16,
7}
8
9pub enum AuthState {
11 Success,
13 Response(Response),
15 NotRequested,
17}
18
19pub trait Authenticator: crate::NextBytes {
21 fn auth_scheme(&self) -> &'static str;
23
24 fn http_incoming_auth<'a, R>(&'a mut self, get_header: R) -> Result<AuthState, Box<dyn std::error::Error>>
29 where
30 R: Fn(&'static str) -> Result<Option<&'a str>, Box<dyn std::error::Error>>
31 {
32 let auth_scheme = self.auth_scheme();
33
34 let auth_bytes = match get_header("Authorization")? {
35 None => {
36 return Ok(AuthState::Response(Response {
38 headers: vec![("WWW-Authenticate", auth_scheme.to_owned())],
39 status_code: 401,
40 }))
41 }
42 Some(header) => {
43 if !header.starts_with(auth_scheme) {
45 return Err(format!("unsupported auth scheme: {}", header))?;
46 }
47 let challenge = header.trim_start_matches(auth_scheme).trim_start();
48 base64::decode(challenge).map_err(|err| format!("Malformed Base64 in Authorization header: {:?}", err))?
49 }
50 };
51 if let Some(next_bytes) = self.next_bytes(Some(&auth_bytes))? {
53 return Ok(AuthState::Response(Response {
54 headers: vec![("WWW-Authenticate", format!("{} {}", auth_scheme, base64::encode(&next_bytes)))],
55 status_code: 401,
56 }));
57 }
58
59 Ok(AuthState::Success)
60 }
61
62 fn http_outgoing_auth<'a, F>(&'a mut self, get_header: F) -> Result<AuthState, Box<dyn std::error::Error>>
68 where
69 F: Fn(&'static str) -> Result<Vec<&'a str>, Box<dyn std::error::Error>>
70 {
71 let methods = get_header("WWW-Authenticate")?;
72 let auth_scheme = self.auth_scheme();
73
74 if methods.contains(&auth_scheme) {
76 if let Some(next_bytes) = self.next_bytes(None)? {
77 return Ok(AuthState::Response(Response {
78 headers: vec![("Authorization", format!("{} {}", auth_scheme, base64::encode(&next_bytes)))],
79 status_code: 0,
80 }));
81 }
82 }
83 else if methods.len() == 1 && methods[0].starts_with(auth_scheme) {
85 let challenge = methods[0].trim_start_matches(auth_scheme).trim_start();
86 let in_bytes = base64::decode(challenge).map_err(|err| format!("Malformed Base64 in WWW-Authenticate header: {:?}", err))?;
87 if let Some(next_bytes) = self.next_bytes(Some(&in_bytes))? {
88 return Ok(AuthState::Response(Response {
89 headers: vec![("Authorization", format!("{} {}", auth_scheme, base64::encode(&next_bytes)))],
90 status_code: 0,
91 }));
92 }
93 return Ok(AuthState::Success);
94 }
95
96 Ok(AuthState::NotRequested)
98 }
99}