use std::ffi::CString;
use std::sync::OnceLock;
use parking_lot::Mutex;
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
use crate::types::SetLogCallbackFunc;
use common::constants::PSK_LEN;
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct SecurePsk(pub [u8; PSK_LEN]);
pub struct ConfigCenter {
pub ra_agent_url: String,
pub kdc_agent_url: String,
pub capath: String,
pub certpath: String,
pub privatepath: String,
pub crlpath: String,
pub key_pwd: Zeroizing<String>,
pub psk: Option<SecurePsk>,
}
impl ConfigCenter {
fn new() -> Self {
Self {
ra_agent_url: String::new(),
kdc_agent_url: String::new(),
capath: String::new(),
certpath: String::new(),
privatepath: String::new(),
crlpath: String::new(),
key_pwd: Zeroizing::new(String::new()),
psk: None,
}
}
}
static CONFIG: OnceLock<Mutex<ConfigCenter>> = OnceLock::new();
pub fn get_config_center() -> &'static Mutex<ConfigCenter> {
CONFIG.get_or_init(|| Mutex::new(ConfigCenter::new()))
}
static LOG_CALLBACK: OnceLock<Mutex<Option<SetLogCallbackFunc>>> = OnceLock::new();
pub fn get_log_callback_holder() -> &'static Mutex<Option<SetLogCallbackFunc>> {
LOG_CALLBACK.get_or_init(|| Mutex::new(None))
}
pub fn proxy_log_impl(severity: i32, msg: &str, file: &str, line: u32) {
let holder = get_log_callback_holder();
let guard = holder.lock();
let cb = *guard;
drop(guard);
if let Some(cb) = cb {
if let (Ok(c_msg), Ok(c_file)) = (CString::new(msg), CString::new(file)) {
cb(severity, c_msg.as_ptr(), c_file.as_ptr(), line as i32);
}
}
}
#[macro_export]
macro_rules! proxy_log {
($severity:expr, $msg:expr) => {
$crate::config_manager::proxy_log_impl($severity, $msg, file!(), line!())
};
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
fn reset_config() {
let config = get_config_center();
let mut guard = config.lock();
guard.ra_agent_url = String::new();
guard.kdc_agent_url = String::new();
guard.capath = String::new();
guard.certpath = String::new();
guard.privatepath = String::new();
guard.crlpath = String::new();
guard.key_pwd = Zeroizing::new(String::new());
guard.psk = None;
}
#[test]
fn test_singleton_returns_valid_reference() {
let config = get_config_center();
let _guard = config.lock();
}
#[test]
fn test_initial_state() {
let config = get_config_center();
let guard = config.lock();
assert!(guard.ra_agent_url.is_empty());
assert!(guard.kdc_agent_url.is_empty());
assert!(guard.capath.is_empty());
assert!(guard.certpath.is_empty());
assert!(guard.privatepath.is_empty());
assert!(guard.crlpath.is_empty());
assert!(guard.key_pwd.is_empty());
assert!(guard.psk.is_none());
}
#[test]
fn test_set_and_get_urls() {
reset_config();
let config = get_config_center();
{
let mut guard = config.lock();
guard.ra_agent_url = "https://ra.example.com".to_string();
guard.kdc_agent_url = "https://kdc.example.com".to_string();
}
let guard = config.lock();
assert_eq!(guard.ra_agent_url, "https://ra.example.com");
assert_eq!(guard.kdc_agent_url, "https://kdc.example.com");
}
#[test]
fn test_psk_lifecycle() {
reset_config();
let config = get_config_center();
{
let guard = config.lock();
assert!(guard.psk.is_none());
}
{
let mut guard = config.lock();
guard.psk = Some(SecurePsk([0u8; PSK_LEN]));
}
{
let guard = config.lock();
assert!(guard.psk.is_some());
assert_eq!(guard.psk.as_ref().unwrap().0, [0u8; PSK_LEN]);
}
{
let mut guard = config.lock();
guard.psk = None;
}
let guard = config.lock();
assert!(guard.psk.is_none());
}
#[test]
fn test_log_callback_holder_initial_state() {
*get_log_callback_holder().lock() = None;
{
let holder = get_log_callback_holder();
let guard = holder.lock();
assert!(guard.is_none());
}
*get_log_callback_holder().lock() = None;
}
#[test]
fn test_log_callback_set_and_get() {
*get_log_callback_holder().lock() = None;
*get_log_callback_holder().lock() = Some(test_log_cb);
{
let holder = get_log_callback_holder();
let guard = holder.lock();
assert!(guard.is_some());
}
*get_log_callback_holder().lock() = None;
}
extern "C" fn test_log_cb(
_severity: i32,
_msg: *const std::os::raw::c_char,
_file: *const std::os::raw::c_char,
_line: i32,
) {
}
#[test]
fn test_proxy_log_impl_no_callback() {
*get_log_callback_holder().lock() = None;
proxy_log_impl(1, "no callback set", "test.rs", 1);
}
#[test]
fn test_proxy_log_impl_with_callback() {
*get_log_callback_holder().lock() = Some(test_log_cb);
proxy_log_impl(1, "with callback", "test.rs", 1);
*get_log_callback_holder().lock() = None;
}
#[test]
fn test_proxy_log_impl_with_nul_message() {
*get_log_callback_holder().lock() = Some(test_log_cb);
proxy_log_impl(1, "hello\0world", "test.rs", 1);
*get_log_callback_holder().lock() = None;
}
}