Move parse_env() and parse_config() to config::loader module

This commit is contained in:
silverpill 2023-01-25 01:26:22 +00:00
parent 8dfb040b5f
commit ce8f597501
3 changed files with 122 additions and 113 deletions

116
src/config/loader.rs Normal file
View file

@ -0,0 +1,116 @@
use std::os::unix::fs::MetadataExt;
use std::path::Path;
use std::str::FromStr;
use rsa::RsaPrivateKey;
use crate::utils::crypto_rsa::{
deserialize_private_key,
generate_rsa_key,
serialize_private_key,
};
use crate::utils::files::{set_file_permissions, write_file};
use super::environment::Environment;
use super::main::Config;
struct EnvConfig {
config_path: String,
environment: Option<Environment>,
}
#[cfg(feature = "production")]
const DEFAULT_CONFIG_PATH: &str = "/etc/mitra/config.yaml";
#[cfg(not(feature = "production"))]
const DEFAULT_CONFIG_PATH: &str = "config.yaml";
fn parse_env() -> EnvConfig {
dotenv::from_filename(".env.local").ok();
dotenv::dotenv().ok();
let config_path = std::env::var("CONFIG_PATH")
.unwrap_or(DEFAULT_CONFIG_PATH.to_string());
let environment = std::env::var("ENVIRONMENT").ok()
.map(|val| Environment::from_str(&val).expect("invalid environment type"));
EnvConfig {
config_path,
environment,
}
}
extern "C" {
fn geteuid() -> u32;
}
fn check_directory_owner(path: &Path) -> () {
let metadata = std::fs::metadata(path)
.expect("can't read file metadata");
let owner_uid = metadata.uid();
let current_uid = unsafe { geteuid() };
if owner_uid != current_uid {
panic!(
"{} owner ({}) is different from the current user ({})",
path.display(),
owner_uid,
current_uid,
);
};
}
/// Generates new instance RSA key or returns existing key
fn read_instance_rsa_key(storage_dir: &Path) -> RsaPrivateKey {
let private_key_path = storage_dir.join("instance_rsa_key");
if private_key_path.exists() {
let private_key_str = std::fs::read_to_string(&private_key_path)
.expect("failed to read instance RSA key");
let private_key = deserialize_private_key(&private_key_str)
.expect("failed to read instance RSA key");
private_key
} else {
let private_key = generate_rsa_key()
.expect("failed to generate RSA key");
let private_key_str = serialize_private_key(&private_key)
.expect("failed to serialize RSA key");
write_file(private_key_str.as_bytes(), &private_key_path)
.expect("failed to write instance RSA key");
set_file_permissions(&private_key_path, 0o600)
.expect("failed to set permissions on RSA key file");
private_key
}
}
pub fn parse_config() -> Config {
let env = parse_env();
let config_yaml = std::fs::read_to_string(&env.config_path)
.expect("failed to load config file");
let mut config = serde_yaml::from_str::<Config>(&config_yaml)
.expect("invalid yaml data");
// Set parameters from environment
config.config_path = env.config_path;
if let Some(environment) = env.environment {
// Overwrite default only if ENVIRONMENT variable is set
config.environment = environment;
};
// Validate config
if !config.storage_dir.exists() {
panic!("storage directory does not exist");
};
check_directory_owner(&config.storage_dir);
config.try_instance_url().expect("invalid instance URI");
if let Some(blockchain_config) = config.blockchain() {
if let Some(ethereum_config) = blockchain_config.ethereum_config() {
ethereum_config.try_ethereum_chain_id().unwrap();
if !ethereum_config.contract_dir.exists() {
panic!("contract directory does not exist");
};
};
};
if config.ipfs_api_url.is_some() != config.ipfs_gateway_url.is_some() {
panic!("both ipfs_api_url and ipfs_gateway_url must be set");
};
// Insert instance RSA key
config.instance_rsa_key = Some(read_instance_rsa_key(&config.storage_dir));
config
}

View file

@ -1,6 +1,4 @@
use std::os::unix::fs::MetadataExt;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::path::PathBuf;
use log::{Level as LogLevel};
use rsa::RsaPrivateKey;
@ -10,41 +8,12 @@ use url::Url;
use crate::activitypub::constants::ACTOR_KEY_SUFFIX;
use crate::activitypub::identifiers::local_instance_actor_id;
use crate::errors::ConversionError;
use crate::utils::crypto_rsa::{
deserialize_private_key,
generate_rsa_key,
serialize_private_key,
};
use crate::utils::files::{set_file_permissions, write_file};
use crate::utils::urls::guess_protocol;
use super::blockchain::BlockchainConfig;
use super::environment::Environment;
use super::MITRA_VERSION;
struct EnvConfig {
config_path: String,
environment: Option<Environment>,
}
#[cfg(feature = "production")]
const DEFAULT_CONFIG_PATH: &str = "/etc/mitra/config.yaml";
#[cfg(not(feature = "production"))]
const DEFAULT_CONFIG_PATH: &str = "config.yaml";
fn parse_env() -> EnvConfig {
dotenv::from_filename(".env.local").ok();
dotenv::dotenv().ok();
let config_path = std::env::var("CONFIG_PATH")
.unwrap_or(DEFAULT_CONFIG_PATH.to_string());
let environment = std::env::var("ENVIRONMENT").ok()
.map(|val| Environment::from_str(&val).expect("invalid environment type"));
EnvConfig {
config_path,
environment,
}
}
fn default_log_level() -> LogLevel { LogLevel::Info }
fn default_login_message() -> String { "Do not sign this message on other sites!".to_string() }
@ -81,7 +50,7 @@ pub struct Config {
pub instance_description: String,
#[serde(skip)]
instance_rsa_key: Option<RsaPrivateKey>,
pub(super) instance_rsa_key: Option<RsaPrivateKey>,
#[serde(default)]
pub registrations_open: bool, // default is false
@ -110,7 +79,7 @@ pub struct Config {
}
impl Config {
fn try_instance_url(&self) -> Result<Url, ConversionError> {
pub(super) fn try_instance_url(&self) -> Result<Url, ConversionError> {
let scheme = match self.environment {
Environment::Development => "http",
Environment::Production => guess_protocol(&self.instance_uri),
@ -201,84 +170,6 @@ impl Instance {
}
}
extern "C" {
fn geteuid() -> u32;
}
fn check_directory_owner(path: &Path) -> () {
let metadata = std::fs::metadata(path)
.expect("can't read file metadata");
let owner_uid = metadata.uid();
let current_uid = unsafe { geteuid() };
if owner_uid != current_uid {
panic!(
"{} owner ({}) is different from the current user ({})",
path.display(),
owner_uid,
current_uid,
);
};
}
/// Generates new instance RSA key or returns existing key
fn read_instance_rsa_key(storage_dir: &Path) -> RsaPrivateKey {
let private_key_path = storage_dir.join("instance_rsa_key");
if private_key_path.exists() {
let private_key_str = std::fs::read_to_string(&private_key_path)
.expect("failed to read instance RSA key");
let private_key = deserialize_private_key(&private_key_str)
.expect("failed to read instance RSA key");
private_key
} else {
let private_key = generate_rsa_key()
.expect("failed to generate RSA key");
let private_key_str = serialize_private_key(&private_key)
.expect("failed to serialize RSA key");
write_file(private_key_str.as_bytes(), &private_key_path)
.expect("failed to write instance RSA key");
set_file_permissions(&private_key_path, 0o600)
.expect("failed to set permissions on RSA key file");
private_key
}
}
pub fn parse_config() -> Config {
let env = parse_env();
let config_yaml = std::fs::read_to_string(&env.config_path)
.expect("failed to load config file");
let mut config = serde_yaml::from_str::<Config>(&config_yaml)
.expect("invalid yaml data");
// Set parameters from environment
config.config_path = env.config_path;
if let Some(environment) = env.environment {
// Overwrite default only if ENVIRONMENT variable is set
config.environment = environment;
};
// Validate config
if !config.storage_dir.exists() {
panic!("storage directory does not exist");
};
check_directory_owner(&config.storage_dir);
config.try_instance_url().expect("invalid instance URI");
if let Some(blockchain_config) = config.blockchain() {
if let Some(ethereum_config) = blockchain_config.ethereum_config() {
ethereum_config.try_ethereum_chain_id().unwrap();
if !ethereum_config.contract_dir.exists() {
panic!("contract directory does not exist");
};
};
};
if config.ipfs_api_url.is_some() != config.ipfs_gateway_url.is_some() {
panic!("both ipfs_api_url and ipfs_gateway_url must be set");
};
// Insert instance RSA key
config.instance_rsa_key = Some(read_instance_rsa_key(&config.storage_dir));
config
}
#[cfg(test)]
mod tests {
use crate::utils::crypto_rsa::generate_weak_rsa_key;

View file

@ -1,5 +1,6 @@
mod blockchain;
mod environment;
mod loader;
mod main;
pub use blockchain::{
@ -8,6 +9,7 @@ pub use blockchain::{
MoneroConfig,
};
pub use environment::Environment;
pub use main::{parse_config, Config, Instance};
pub use loader::parse_config;
pub use main::{Config, Instance};
pub const MITRA_VERSION: &str = env!("CARGO_PKG_VERSION");