use crate::helper::string::{DisplayOnlyForDebugging, Obfuscate};
use super::Authenticator;
use async_trait::async_trait;
use base64::Engine;
use http::header;
use serde::{Deserialize, Serialize};
use std::{
fmt,
io::{Error, ErrorKind, Result},
};
#[derive(Deserialize, Serialize, Clone)]
#[serde(default, deny_unknown_fields)]
pub struct Basic {
#[serde(alias = "usr")]
#[serde(alias = "user")]
pub username: String,
#[serde(alias = "pwd")]
#[serde(alias = "pass")]
pub password: String,
}
impl Default for Basic {
fn default() -> Self {
Basic {
username: "".to_owned(),
password: "".to_owned(),
}
}
}
impl fmt::Debug for Basic {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Basic")
.field("username", &self.username)
.field(
"password",
&self.password.to_obfuscate().display_only_for_debugging(),
)
.finish()
}
}
impl Basic {
pub fn new(username: &str, password: &str) -> Self {
Basic {
username: username.to_string(),
password: password.to_string(),
}
}
}
#[async_trait]
impl Authenticator for Basic {
async fn authenticate(&self) -> Result<(Vec<u8>, Vec<u8>)> {
if let ("", "") = (self.username.as_ref(), self.password.as_ref()) {
return Err(Error::new(
ErrorKind::InvalidData,
"Basic authentification require a username and a password",
));
}
let basic = base64::engine::general_purpose::STANDARD
.encode(format!("{}:{}", self.username, self.password));
Ok((
header::AUTHORIZATION.as_str().as_bytes().to_vec(),
format!("Basic {}", basic).as_bytes().to_vec(),
))
}
}
#[cfg(test)]
mod tests {
use super::*;
use macro_rules_attribute::apply;
use smol_macros::test;
#[apply(test!)]
async fn authenticate() {
let username = "my_username";
let password = "my_password";
let token_expected = "Basic bXlfdXNlcm5hbWU6bXlfcGFzc3dvcmQ=";
let (auth_name, auth_value) = Basic::new(username, password).authenticate().await.unwrap();
assert_eq!(auth_name, "authorization".to_string().into_bytes());
assert_eq!(token_expected.as_bytes(), auth_value);
}
}