use async_trait::async_trait;
use super::super::auth_strategy::{AuthConfig, AuthStrategy, Credentials};
use super::super::errors::AuthError;
#[derive(Debug, Default)]
pub struct WindowsAuthStrategy;
#[async_trait]
impl AuthStrategy for WindowsAuthStrategy {
async fn authenticate(&self, config: &AuthConfig) -> anyhow::Result<Credentials> {
let username = config.get("username").ok_or_else(|| {
AuthError::MissingCredentials(
"username (optionally DOMAIN\\user), password".to_string(),
)
})?;
let password = config
.get("password")
.ok_or_else(|| AuthError::MissingCredentials("username, password".to_string()))?;
let mut credentials = Credentials::new();
credentials.insert("username".to_string(), username.clone());
credentials.insert("password".to_string(), password.clone());
Ok(credentials)
}
fn validate_credentials(&self, credentials: &Credentials) -> bool {
credentials
.get("username")
.is_some_and(|username| !username.is_empty())
&& credentials
.get("password")
.is_some_and(|password| !password.is_empty())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn carries_a_domain_qualified_username_through_unchanged() {
let strategy = WindowsAuthStrategy;
let config = AuthConfig::from([
("username".to_string(), "CORP\\alice".to_string()),
("password".to_string(), "s3cr3t".to_string()),
]);
let credentials = strategy.authenticate(&config).await.unwrap();
assert_eq!(credentials.get("username").unwrap(), "CORP\\alice");
}
#[tokio::test]
async fn rejects_a_config_missing_either_field() {
let strategy = WindowsAuthStrategy;
assert!(strategy.authenticate(&AuthConfig::new()).await.is_err());
}
}