use std::convert::TryFrom;
use crate::ChallengeRef;
pub fn encode_credentials(username: &str, password: &str) -> String {
let user_pass = format!("{}:{}", username, password);
const PREFIX: &str = "Basic ";
let mut value = String::with_capacity(PREFIX.len() + base64_encoded_len(user_pass.len()));
value.push_str(PREFIX);
base64::encode_config_buf(&user_pass[..], base64::STANDARD, &mut value);
value
}
fn base64_encoded_len(input_len: usize) -> usize {
(input_len + 2) / 3 * 4
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct BasicClient {
realm: Box<str>,
}
impl BasicClient {
pub fn realm(&self) -> &str {
&*self.realm
}
#[inline]
pub fn respond(&self, username: &str, password: &str) -> String {
encode_credentials(username, password)
}
}
impl TryFrom<&ChallengeRef<'_>> for BasicClient {
type Error = String;
fn try_from(value: &ChallengeRef<'_>) -> Result<Self, Self::Error> {
if !value.scheme.eq_ignore_ascii_case("Basic") {
return Err(format!(
"BasicClient doesn't support challenge scheme {:?}",
value.scheme
));
}
let mut realm = None;
for (k, v) in &value.params {
if k.eq_ignore_ascii_case("realm") {
realm = Some(v.to_unescaped());
}
}
let realm = realm.ok_or("missing required parameter realm")?;
Ok(BasicClient {
realm: realm.into_boxed_str(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic() {
let ctx = BasicClient {
realm: "WallyWorld".into(),
};
assert_eq!(
ctx.respond("Aladdin", "open sesame"),
"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
);
let ctx = BasicClient {
realm: "foo".into(),
};
assert_eq!(ctx.respond("test", "123\u{A3}"), "Basic dGVzdDoxMjPCow==");
}
}