use std::collections::HashMap;
use crate::{
BasicAuthCred, StaticTokenAuthCred, StaticTokenAuthCredsConfig,
config::BasicAuthCredsConfig,
error::{CredsError, CredsResult},
};
pub trait BasicAuthCredsValidator<Cred>
where
Cred: BasicAuthCred,
{
fn get_cred(&self, username: &str) -> CredsResult<Option<&Cred>>;
fn verify_cred(&self, username: &str, password: &str) -> CredsResult<Option<&Cred>> {
let Some(cred) = self.get_cred(username)? else {
return Ok(None);
};
if cred.verify_password(password)? {
Ok(Some(cred))
} else {
Ok(None)
}
}
}
pub struct MapBasicAuthCredsValidator<Creds>
where
Creds: BasicAuthCred + Clone,
{
pub creds: HashMap<String, Creds>,
}
impl<Creds> MapBasicAuthCredsValidator<Creds>
where
Creds: BasicAuthCred + Clone,
{
pub fn from_config(config: &BasicAuthCredsConfig<Creds>) -> CredsResult<Self> {
config.validate()?;
Ok(Self {
creds: config
.users
.iter()
.map(|creds| (creds.username().to_string(), creds.clone()))
.collect(),
})
}
}
impl<Creds> BasicAuthCredsValidator<Creds> for MapBasicAuthCredsValidator<Creds>
where
Creds: BasicAuthCred + Clone,
{
fn get_cred(&self, username: &str) -> CredsResult<Option<&Creds>> {
Ok(self.creds.get(username))
}
}
pub trait StaticTokenAuthCredsValidator<Cred>
where
Cred: StaticTokenAuthCred,
{
fn get_cred(&self, token: &str) -> CredsResult<Option<&Cred>>;
fn verify_cred(&self, token: &str) -> CredsResult<Option<&Cred>> {
let cred = self
.get_cred(token)?
.ok_or(CredsError::InvalidStaticTokenCredentials)?;
cred.verify_token(token)?;
Ok(Some(cred))
}
}
pub struct MapStaticTokenAuthCredsValidator<Creds>
where
Creds: StaticTokenAuthCred + Clone,
{
pub creds: HashMap<String, Creds>,
}
impl<Creds> MapStaticTokenAuthCredsValidator<Creds>
where
Creds: StaticTokenAuthCred + Clone,
{
pub fn from_config(config: &StaticTokenAuthCredsConfig<Creds>) -> CredsResult<Self> {
config.validate()?;
Ok(Self {
creds: config
.tokens
.iter()
.map(|creds| (creds.token_hash().to_string(), creds.clone()))
.collect(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Argon2BasicAuthCred, BasicAuthCred};
fn test_config() -> BasicAuthCredsConfig<Argon2BasicAuthCred> {
BasicAuthCredsConfig {
users: vec![
Argon2BasicAuthCred::new("admin".to_string(), "secret123".to_string()).unwrap(),
Argon2BasicAuthCred::new("user".to_string(), "password".to_string()).unwrap(),
],
}
}
#[test]
fn test_validate_credentials() -> CredsResult<()> {
let validator = MapBasicAuthCredsValidator::from_config(&test_config()).unwrap();
assert!(validator.verify_cred("admin", "secret123")?.is_some());
assert!(validator.verify_cred("user", "password")?.is_some());
assert!(validator.verify_cred("admin", "wrong")?.is_none());
assert!(validator.verify_cred("unknown", "password")?.is_none());
Ok(())
}
#[test]
fn test_get_display_name() -> CredsResult<()> {
let validator = MapBasicAuthCredsValidator::from_config(&test_config()).unwrap();
assert_eq!(
validator.get_cred("admin")?.map(|c| c.display_name()),
Some("admin")
);
assert_eq!(
validator.get_cred("user")?.map(|c| c.display_name()),
Some("user")
);
assert!(validator.get_cred("unknown")?.is_none());
Ok(())
}
}