#![forbid(unsafe_code)]
#![deny(missing_docs)]
use crate::errors::ExporterError;
use serde::Deserialize;
use std::collections::HashMap;
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
use std::str::FromStr;
const INVALID_USERNAME_CHARS: &[char] = &[
'\u{00}', '\u{01}', '\u{02}', '\u{03}', '\u{04}', '\u{05}',
'\u{06}', '\u{07}', '\u{08}', '\u{09}',
'\u{0a}', '\u{0b}', '\u{0c}', '\u{0d}', '\u{0e}', '\u{0f}',
'\u{10}', '\u{11}', '\u{12}', '\u{13}', '\u{14}', '\u{15}',
'\u{16}', '\u{17}', '\u{18}', '\u{19}',
'\u{1a}', '\u{1b}', '\u{1c}', '\u{1d}', '\u{1e}', '\u{1f}',
'\u{7f}',
':',
];
#[derive(Clone, Debug, Default, Deserialize)]
pub struct BasicAuthConfig {
pub basic_auth_users: Option<HashMap<String, String>>,
}
impl BasicAuthConfig {
pub fn from_yaml(path: &Path) -> Result<Self, ExporterError> {
let file = File::open(path)?;
let reader = BufReader::new(file);
let config: Self = serde_yaml::from_reader(reader)?;
config.validate()?;
Ok(config)
}
pub fn has_users(&self) -> bool {
self.basic_auth_users.is_some()
}
fn validate(&self) -> Result<(), ExporterError> {
let users = match &self.basic_auth_users {
None => return Ok(()),
Some(users) => users,
};
for (username, hashed_password) in users {
let invalid_username = username
.chars()
.any(|c| INVALID_USERNAME_CHARS.contains(&c));
if invalid_username {
let err = ExporterError::InvalidUsername(username.into());
return Err(err);
}
if let Err(err) = bcrypt::HashParts::from_str(hashed_password) {
let msg = format!(
"bcrypt error '{err}' when validating user {username}",
);
let err = ExporterError::BcryptValidationError(msg);
return Err(err);
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_user_config_from_yaml_invalid() {
let path = Path::new("test-data/config_invalid.yaml");
let config = BasicAuthConfig::from_yaml(&path);
assert!(config.is_err());
}
#[test]
fn basic_user_config_from_yaml_null() {
let path = Path::new("test-data/config_null.yaml");
let config = BasicAuthConfig::from_yaml(&path);
assert!(config.is_ok());
}
#[test]
fn basic_user_config_from_yaml_ok() {
let path = Path::new("test-data/config_ok.yaml");
let config = BasicAuthConfig::from_yaml(&path);
assert!(config.is_ok());
}
}