use std::path::PathBuf;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AdminTlsConfig {
pub server_cert: PathBuf,
pub server_key: PathBuf,
pub client_ca: PathBuf,
#[serde(default)]
pub allowed_clients: Vec<String>,
#[serde(default = "default_admin_port")]
pub port: u16,
}
fn default_admin_port() -> u16 {
8443
}
impl Default for AdminTlsConfig {
fn default() -> Self {
Self {
server_cert: PathBuf::from("admin/server.crt"),
server_key: PathBuf::from("admin/server.key"),
client_ca: PathBuf::from("admin/ca.crt"),
allowed_clients: Vec::new(),
port: default_admin_port(),
}
}
}
impl AdminTlsConfig {
pub fn validate(&self) -> anyhow::Result<()> {
for (label, path) in [
("server_cert", &self.server_cert),
("server_key", &self.server_key),
("client_ca", &self.client_ca),
] {
if !path.exists() {
return Err(anyhow::anyhow!(
"admin TLS {label} not found: {}",
path.display()
));
}
}
Ok(())
}
pub fn is_client_allowed(&self, cn: &str) -> bool {
if self.allowed_clients.is_empty() {
return true;
}
self.allowed_clients
.iter()
.any(|pattern| pattern == cn || pattern == "*")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_port_is_8443() {
let config = AdminTlsConfig::default();
assert_eq!(config.port, 8443);
}
#[test]
fn empty_allowed_clients_allows_anyone() {
let config = AdminTlsConfig::default();
assert!(config.is_client_allowed("anything"));
}
#[test]
fn rejects_unlisted_client() {
let config = AdminTlsConfig {
allowed_clients: vec!["CN=admin".into()],
..Default::default()
};
assert!(config.is_client_allowed("CN=admin"));
assert!(!config.is_client_allowed("CN=hacker"));
}
#[test]
fn wildcard_allows_all() {
let config = AdminTlsConfig {
allowed_clients: vec!["*".into()],
..Default::default()
};
assert!(config.is_client_allowed("anyone"));
}
#[test]
fn validate_fails_for_missing_certs() {
let config = AdminTlsConfig {
server_cert: "/nonexistent/server.crt".into(),
server_key: "/nonexistent/server.key".into(),
client_ca: "/nonexistent/ca.crt".into(),
..Default::default()
};
assert!(config.validate().is_err());
}
}