peach-workspace/peach-lib/src/config_manager.rs

240 lines
8.5 KiB
Rust

//! Interfaces for writing and reading PeachCloud configurations, stored in yaml.
//!
//! Different PeachCloud microservices import peach-lib, so that they can share
//! this interface.
//!
//! Config values are looked up from three locations in this order by key name:
//! 1. from environmental variables
//! 2. from a configuration file
//! 3. from default values
//!
//! The configuration file is located at: "/var/lib/peachcloud/config.yml"
//! unless its path is configured by setting PEACH_CONFIG_PATH env variable.
use std::{env, fs};
use std::collections::HashMap;
use fslock::LockFile;
use lazy_static::lazy_static;
use crate::error::PeachError;
// load path to main configuration file
// from PEACH_CONFIG_PATH if that environment variable is set
// or using the default value if not set
pub const DEFAULT_YAML_PATH: &str = "/var/lib/peachcloud/config.yml";
lazy_static! {
static ref CONFIG_PATH: String = {
if let Ok(val) = env::var("PEACH_CONFIG_PATH") {
val
}
else {
DEFAULT_YAML_PATH.to_string()
}
};
// lock file (used to avoid race conditions during config reading & writing)
// the lock file path is the config file path + ".lock"
static ref LOCK_FILE_PATH: String = format!("{}.lock", *CONFIG_PATH);
}
// primary interface for getting config values
pub fn get_config_value(key: &str) -> Result<String, PeachError> {
// first check if it is an environmental variable
if let Ok(val) = env::var(key) {
Ok(val)
} else {
// then check disc
let peach_config_on_disc = load_peach_config_from_disc()?;
let val = peach_config_on_disc.get(key);
// then check defaults
match val {
Some(v) => Ok(v.to_string()),
None => {
match get_peach_config_defaults().get(key) {
Some(v) => Ok(v.to_string()),
None => {
Err(PeachError::InvalidKey { msg: format!("No default config value set for key: {}", key) })
}
}
}
}
}
}
// Default values for PeachCloud configs which are used for any key which is not set
// via an environment variable or in a saved configuration file.
pub fn get_peach_config_defaults() -> HashMap<String, String> {
let peach_config_defaults: HashMap<String, String> = HashMap::from([
("STANDALONE_MODE".to_string(), "true".to_string()),
("DISABLE_AUTH".to_string(), "false".to_string()),
("ADDR".to_string(), "127.0.0.1".to_string()),
("PORT".to_string(), "8000".to_string()),
("EXTERNAL_DOMAIN".to_string(), "".to_string()),
("DYN_DOMAIN".to_string(), "".to_string()),
("DYN_DNS_SERVER_ADDRESS".to_string(), "http://dynserver.dyn.peachcloud.org".to_string()),
("DYN_USE_CUSTOM_SERVER".to_string(), "true".to_string()),
("DYN_TSIG_KEY_PATH".to_string(), "".to_string()),
("DYN_NAMESERVER".to_string(), "ns.peachcloud.org".to_string()),
("DYN_ENABLED".to_string(), "false".to_string()),
("SSB_ADMIN_IDS".to_string(), "[]".to_string()),
("ADMIN_PASSWORD_HASH".to_string(), "146".to_string()),
("TEMPORARY_PASSWORD_HASH".to_string(), "".to_string()),
("GO_SBOT_DATADIR".to_string(), "".to_string()),
("PEACH_CONFIGDIR".to_string(), "/var/lib/peachcloud".to_string()),
]);
peach_config_defaults
}
// helper function to load PeachCloud configuration files saved to disc
pub fn load_peach_config_from_disc() -> Result<HashMap<String, String>, PeachError> {
let peach_config : HashMap<String, String> = HashMap::new();
// TODO: implement
Ok(peach_config)
}
pub fn save_peach_config_to_disc(peach_config: HashMap<String, String>) -> Result<HashMap<String, String>, PeachError> {
// use a file lock to avoid race conditions while saving config
let mut lock = LockFile::open(&*LOCK_FILE_PATH)?;
lock.lock()?;
// convert HashMap to yaml
let yaml_str = serde_yaml::to_string(&peach_config)?;
// write yaml to file
fs::write(CONFIG_PATH.as_str(), yaml_str).map_err(|source| PeachError::Write {
source,
path: CONFIG_PATH.to_string(),
})?;
// unlock file lock
lock.unlock()?;
// return modified HashMap
Ok(peach_config)
}
// helper functions for serializing and deserializing PeachConfig from disc
pub fn save_peach_config_value(key: &str, value: String) -> Result<HashMap<String, String>, PeachError> {
// get current config from disc
let mut peach_config = load_peach_config_from_disc()?;
// insert new key/value
peach_config.insert(key.to_string(), value);
// save hte modified hashmap to disc
save_peach_config_to_disc(peach_config)
}
// set all dyn configuration values at once
pub fn set_peach_dyndns_config(
dyn_domain: &str,
dyn_dns_server_address: &str,
dyn_tsig_key_path: &str,
dyn_enabled: bool,
) -> Result<HashMap<String, String>, PeachError> {
let mut peach_config = load_peach_config_from_disc()?;
let dyn_enabled_str = match dyn_enabled {
true => "true",
false => "false"
};
peach_config.insert("DYN_DOMAIN".to_string(), dyn_domain.to_string());
peach_config.insert("DYN_DNS_SERVER_ADDRESS".to_string(), dyn_dns_server_address.to_string());
peach_config.insert("DYN_TSIG_KEY_PATH".to_string(), dyn_tsig_key_path.to_string());
peach_config.insert("DYN_ENABLED".to_string(), dyn_enabled_str.to_string());
save_peach_config_to_disc(peach_config)
}
pub fn set_external_domain(new_external_domain: &str) -> Result<HashMap<String, String>, PeachError> {
save_peach_config_value("EXTERNAL_DOMAIN", new_external_domain.to_string())
}
pub fn get_peachcloud_domain() -> Result<Option<String>, PeachError> {
let external_domain = get_config_value("EXTERNAL_DOMAIN")?;
let dyn_domain = get_config_value("DYN_DOMAIN")?;
if !external_domain.is_empty() {
Ok(Some(external_domain.to_string()))
} else if !dyn_domain.is_empty() {
Ok(Some(dyn_domain.to_string()))
} else {
Ok(None)
}
}
pub fn get_dyndns_server_address() -> Result<String, PeachError> {
get_config_value("DYN_DNS_SERVER_ADDRESS")
}
pub fn set_dyndns_enabled_value(enabled_value: bool) -> Result<HashMap<String, String>, PeachError> {
match enabled_value {
true => save_peach_config_value("DYN_ENABLED", "true".to_string()),
false => save_peach_config_value("DYN_ENABLED", "false".to_string())
}
}
pub fn get_dyndns_enabled_value() -> Result<bool, PeachError> {
let val = get_config_value("DYN_ENABLED")?;
return Ok(val == "true")
}
pub fn add_ssb_admin_id(ssb_id: &str) -> Result<Vec<String>, PeachError> {
let mut ssb_admin_ids = get_ssb_admin_ids()?;
ssb_admin_ids.push(ssb_id.to_string());
save_ssb_admin_ids(ssb_admin_ids)
}
pub fn delete_ssb_admin_id(ssb_id: &str) -> Result<Vec<String>, PeachError> {
let mut ssb_admin_ids = get_ssb_admin_ids()?;
let index_result = ssb_admin_ids.iter().position(|x| *x == ssb_id);
match index_result {
Some(index) => {
ssb_admin_ids.remove(index);
save_ssb_admin_ids(ssb_admin_ids)
}
None => Err(PeachError::SsbAdminIdNotFound {
id: ssb_id.to_string(),
}),
}
}
pub fn save_ssb_admin_ids(ssb_admin_ids: Vec<String>) -> Result<Vec<String>, PeachError> {
// save_peach_config_value("SSB_ADMIN_IDS", ssb_admin_ids.to_string())
// TODO: implement
Ok(ssb_admin_ids)
}
pub fn set_admin_password_hash(password_hash: String) -> Result<HashMap<String, String>, PeachError> {
save_peach_config_value("ADMIN_PASSWORD_HASH", password_hash)
}
pub fn get_admin_password_hash() -> Result<String, PeachError> {
let admin_password_hash = get_config_value("ADMIN_PASSWORD_HASH")?;
if !admin_password_hash.is_empty() {
Ok(admin_password_hash.to_string())
} else {
Err(PeachError::PasswordNotSet)
}
}
pub fn set_temporary_password_hash(password_hash: String) -> Result<HashMap<String, String>, PeachError> {
save_peach_config_value("TEMPORARY_PASSWORD_HASH", password_hash)
}
pub fn get_temporary_password_hash() -> Result<String, PeachError> {
let admin_password_hash = get_config_value("TEMPORARY_PASSWORD_HASH")?;
if !admin_password_hash.is_empty() {
Ok(admin_password_hash.to_string())
} else {
Err(PeachError::PasswordNotSet)
}
}
pub fn get_ssb_admin_ids() -> Result<Vec<String>, PeachError> {
let mut ssb_admin_ids = vec!["x".to_string(), "y".to_string(), "z".to_string()];
Ok(ssb_admin_ids)
}