use async_trait::async_trait;
use base64::Engine;
use base64::engine::general_purpose::STANDARD;
use super::super::auth_strategy::{AuthConfig, AuthStrategy, Credentials};
use super::super::errors::AuthError;
#[derive(Debug, Default)]
pub struct BasicAuthStrategy;
#[async_trait]
impl AuthStrategy for BasicAuthStrategy {
async fn authenticate(&self, config: &AuthConfig) -> anyhow::Result<Credentials> {
let username = config
.get("username")
.ok_or_else(|| AuthError::MissingCredentials("username, password".to_string()))?;
let password = config
.get("password")
.ok_or_else(|| AuthError::MissingCredentials("username, password".to_string()))?;
let token = STANDARD.encode(format!("{username}:{password}"));
let mut credentials = Credentials::new();
credentials.insert("username".to_string(), username.clone());
credentials.insert("password".to_string(), password.clone());
credentials.insert("authorization_header".to_string(), format!("Basic {token}"));
Ok(credentials)
}
fn validate_credentials(&self, credentials: &Credentials) -> bool {
credentials.contains_key("authorization_header")
}
}
#[cfg(test)]
mod tests {
use super::*;
fn config(username: &str, password: &str) -> AuthConfig {
AuthConfig::from([
("username".to_string(), username.to_string()),
("password".to_string(), password.to_string()),
])
}
#[tokio::test]
async fn encodes_username_and_password_as_a_basic_authorization_header() {
let strategy = BasicAuthStrategy;
let credentials = strategy
.authenticate(&config("alice", "s3cr3t"))
.await
.unwrap();
assert_eq!(
credentials.get("authorization_header").unwrap(),
"Basic YWxpY2U6czNjcjN0"
);
}
#[tokio::test]
async fn rejects_a_config_missing_either_field() {
let strategy = BasicAuthStrategy;
assert!(strategy.authenticate(&AuthConfig::new()).await.is_err());
}
#[test]
fn validates_credentials_carrying_an_authorization_header() {
let strategy = BasicAuthStrategy;
let credentials =
Credentials::from([("authorization_header".to_string(), "Basic abc".to_string())]);
assert!(strategy.validate_credentials(&credentials));
assert!(!strategy.validate_credentials(&Credentials::new()));
}
}