294 lines
10 KiB
Rust
294 lines
10 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::collections::{BTreeMap, HashMap};
|
|
use std::{env, fs};
|
|
|
|
use fslock::LockFile;
|
|
use lazy_static::lazy_static;
|
|
use log::debug;
|
|
|
|
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);
|
|
}
|
|
|
|
// 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<&str, &str> = HashMap::from([
|
|
("STANDALONE_MODE", "true"),
|
|
("DISABLE_AUTH", "false"),
|
|
("ADDR", "127.0.0.1"),
|
|
("PORT", "8000"),
|
|
("EXTERNAL_DOMAIN", ""),
|
|
("DYN_DOMAIN", ""),
|
|
(
|
|
"DYN_DNS_SERVER_ADDRESS",
|
|
"http://dynserver.dyn.peachcloud.org",
|
|
),
|
|
("DYN_USE_CUSTOM_SERVER", "true"),
|
|
("DYN_TSIG_KEY_PATH", ""),
|
|
("DYN_NAMESERVER", "ns.peachcloud.org"),
|
|
("DYN_ENABLED", "false"),
|
|
("SSB_ADMIN_IDS", ""),
|
|
("ADMIN_PASSWORD_HASH", "47"),
|
|
("TEMPORARY_PASSWORD_HASH", ""),
|
|
("GO_SBOT_DATADIR", "/home/peach/.ssb-go"),
|
|
("PEACH_CONFIGDIR", "/var/lib/peachcloud"),
|
|
("PEACH_HOMEDIR", "/home/peach"),
|
|
("PEACH_WEBDIR", "/usr/share/peach-web"),
|
|
]);
|
|
// convert HashMap<&str, &str> to HashMap<String, String> and return
|
|
let pc_defaults: HashMap<String, String> = peach_config_defaults
|
|
.iter()
|
|
.map(|(key, val)| (key.to_string(), val.to_string()))
|
|
.collect();
|
|
pc_defaults
|
|
}
|
|
|
|
// primary interface for getting config values
|
|
// 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
|
|
pub fn get_config_value(key: &str) -> Result<String, PeachError> {
|
|
// first check if there is an environmental variable set
|
|
if let Ok(val) = env::var(key) {
|
|
Ok(val)
|
|
} else {
|
|
// then check if a value is set in the config file
|
|
let peach_config_on_disc = load_peach_config_from_disc()?;
|
|
let val = peach_config_on_disc.get(key);
|
|
// if no value is found in the config file, then get the default value
|
|
match val {
|
|
// return config value
|
|
Some(v) => Ok(v.to_string()),
|
|
// get default value
|
|
None => {
|
|
match get_peach_config_defaults().get(key) {
|
|
Some(v) => Ok(v.to_string()),
|
|
// if this key was not found in the defaults, then it was an invalid key
|
|
None => Err(PeachError::InvalidKey {
|
|
key: key.to_string(),
|
|
}),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// helper function to load PeachCloud configuration file saved to disc
|
|
pub fn load_peach_config_from_disc() -> Result<HashMap<String, String>, PeachError> {
|
|
let peach_config_exists = std::path::Path::new(CONFIG_PATH.as_str()).exists();
|
|
// if config file does not exist, return an emtpy HashMap
|
|
if !peach_config_exists {
|
|
let peach_config: HashMap<String, String> = HashMap::new();
|
|
Ok(peach_config)
|
|
}
|
|
// otherwise we load peach config from disk
|
|
else {
|
|
debug!("Loading peach config: {} exists", CONFIG_PATH.as_str());
|
|
let contents =
|
|
fs::read_to_string(CONFIG_PATH.as_str()).map_err(|source| PeachError::Read {
|
|
source,
|
|
path: CONFIG_PATH.to_string(),
|
|
})?;
|
|
let peach_config: HashMap<String, String> = serde_yaml::from_str(&contents)?;
|
|
Ok(peach_config)
|
|
}
|
|
}
|
|
|
|
// helper function to save PeachCloud configuration file to disc
|
|
// takes in a Hashmap<String, String> and saves the whole HashMap as a yaml file
|
|
// with the keys in alphabetical order
|
|
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()?;
|
|
|
|
// first convert Hashmap to BTreeMap (so that keys are saved in deterministic alphabetical order)
|
|
let ordered: BTreeMap<_, _> = peach_config.iter().collect();
|
|
// then serialize BTreeMap as yaml
|
|
let yaml_str = serde_yaml::to_string(&ordered)?;
|
|
|
|
// 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 values from disc
|
|
pub fn save_config_value(key: &str, value: &str) -> 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.to_string());
|
|
|
|
// save the 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_config_value("EXTERNAL_DOMAIN", new_external_domain)
|
|
}
|
|
|
|
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))
|
|
} else if !dyn_domain.is_empty() {
|
|
Ok(Some(dyn_domain))
|
|
} 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_config_value("DYN_ENABLED", "true"),
|
|
false => save_config_value("DYN_ENABLED", "false"),
|
|
}
|
|
}
|
|
|
|
pub fn get_dyndns_enabled_value() -> Result<bool, PeachError> {
|
|
let val = get_config_value("DYN_ENABLED")?;
|
|
Ok(val == "true")
|
|
}
|
|
|
|
pub fn set_admin_password_hash(
|
|
password_hash: String,
|
|
) -> Result<HashMap<String, String>, PeachError> {
|
|
save_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)
|
|
} else {
|
|
Err(PeachError::PasswordNotSet)
|
|
}
|
|
}
|
|
|
|
pub fn set_temporary_password_hash(
|
|
password_hash: String,
|
|
) -> Result<HashMap<String, String>, PeachError> {
|
|
save_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)
|
|
} else {
|
|
Err(PeachError::PasswordNotSet)
|
|
}
|
|
}
|
|
|
|
// add ssb_id to vector of admin ids and save new value for SSB_ADMIN_IDS
|
|
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)
|
|
}
|
|
|
|
// remove ssb_id from vector of admin ids if found and save new value for SSB_ADMIN_IDS
|
|
// if value is not found then return an error
|
|
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(),
|
|
}),
|
|
}
|
|
}
|
|
|
|
// looks up the String value for SSB_ADMIN_IDS and converts it into a Vec<String>
|
|
pub fn get_ssb_admin_ids() -> Result<Vec<String>, PeachError> {
|
|
let ssb_admin_ids_str = get_config_value("SSB_ADMIN_IDS")?;
|
|
let ssb_admin_ids: Vec<String> = serde_json::from_str(&ssb_admin_ids_str)?;
|
|
Ok(ssb_admin_ids)
|
|
}
|
|
|
|
// takes in a Vec<String> and saves SSB_ADMIN_IDS as a json string representation of this vec
|
|
pub fn save_ssb_admin_ids(ssb_admin_ids: Vec<String>) -> Result<Vec<String>, PeachError> {
|
|
let ssb_admin_ids_as_json_str = serde_json::to_string(&ssb_admin_ids)?;
|
|
save_config_value("SSB_ADMIN_IDS", &ssb_admin_ids_as_json_str)?;
|
|
Ok(ssb_admin_ids)
|
|
}
|