pub struct Response {
pub headers: Vec<(&'static str, String)>,
pub status_code: u16,
}
pub enum AuthState {
Success,
Response(Response),
NotRequested,
}
pub trait Authenticator: crate::NextBytes {
fn auth_scheme(&self) -> &'static str;
fn http_incoming_auth<'a, R>(&'a mut self, get_header: R) -> Result<AuthState, Box<dyn std::error::Error>>
where
R: Fn(&'static str) -> Result<Option<&'a str>, Box<dyn std::error::Error>>
{
let auth_scheme = self.auth_scheme();
let auth_bytes = match get_header("Authorization")? {
None => {
return Ok(AuthState::Response(Response {
headers: vec![("WWW-Authenticate", auth_scheme.to_owned())],
status_code: 401,
}))
}
Some(header) => {
if !header.starts_with(auth_scheme) {
return Err(format!("unsupported auth scheme: {}", header))?;
}
let challenge = header.trim_start_matches(auth_scheme).trim_start();
base64::decode(challenge).map_err(|err| format!("Malformed Base64 in Authorization header: {:?}", err))?
}
};
if let Some(next_bytes) = self.next_bytes(Some(&auth_bytes))? {
return Ok(AuthState::Response(Response {
headers: vec![("WWW-Authenticate", format!("{} {}", auth_scheme, base64::encode(&next_bytes)))],
status_code: 401,
}));
}
Ok(AuthState::Success)
}
fn http_outgoing_auth<'a, F>(&'a mut self, get_header: F) -> Result<AuthState, Box<dyn std::error::Error>>
where
F: Fn(&'static str) -> Result<Vec<&'a str>, Box<dyn std::error::Error>>
{
let methods = get_header("WWW-Authenticate")?;
let auth_scheme = self.auth_scheme();
if methods.contains(&auth_scheme) {
if let Some(next_bytes) = self.next_bytes(None)? {
return Ok(AuthState::Response(Response {
headers: vec![("Authorization", format!("{} {}", auth_scheme, base64::encode(&next_bytes)))],
status_code: 0,
}));
}
}
else if methods.len() == 1 && methods[0].starts_with(auth_scheme) {
let challenge = methods[0].trim_start_matches(auth_scheme).trim_start();
let in_bytes = base64::decode(challenge).map_err(|err| format!("Malformed Base64 in WWW-Authenticate header: {:?}", err))?;
if let Some(next_bytes) = self.next_bytes(Some(&in_bytes))? {
return Ok(AuthState::Response(Response {
headers: vec![("Authorization", format!("{} {}", auth_scheme, base64::encode(&next_bytes)))],
status_code: 0,
}));
}
return Ok(AuthState::Success);
}
Ok(AuthState::NotRequested)
}
}