add configuration support for multiple domains

This commit is contained in:
BlockListed 2023-09-09 08:50:45 +02:00
parent 79ce5b49bc
commit 80d3c61cc2
No known key found for this signature in database
GPG Key ID: 2D204777C477B588

View File

@ -1,6 +1,7 @@
use std::env::consts::EXE_SUFFIX;
use std::{env::consts::EXE_SUFFIX, collections::HashMap};
use std::process::exit;
use std::sync::RwLock;
use std::sync::OnceLock;
use job_scheduler_ng::Schedule;
use once_cell::sync::Lazy;
@ -47,6 +48,10 @@ macro_rules! make_config {
_usr: ConfigBuilder,
_overrides: Vec<String>,
domain_hostmap: OnceLock<HostHashMap>,
domain_origins: OnceLock<HostHashMap>,
domain_paths: OnceLock<HostHashMap>,
}
#[derive(Clone, Default, Deserialize, Serialize)]
@ -135,13 +140,20 @@ macro_rules! make_config {
fn build(&self) -> ConfigItems {
let mut config = ConfigItems::default();
let _domain_set = self.domain.is_some();
let _domain_set = self.domain_change_back.is_some();
$($(
config.$name = make_config!{ @build self.$name.clone(), &config, $none_action, $($default)? };
)+)+
config.domain_set = _domain_set;
config.domain = config.domain.trim_end_matches('/').to_string();
config.domain_change_back = config.domain_change_back.split(',').map(|d| d.trim_end_matches('/')).fold(String::new(), |acc, d| {
acc.push_str(d);
acc.push(',');
acc
});
// Remove trailing comma
config.domain_change_back.pop();
config.signups_domains_whitelist = config.signups_domains_whitelist.trim().to_lowercase();
config.org_creation_users = config.org_creation_users.trim().to_lowercase();
@ -335,6 +347,8 @@ macro_rules! make_config {
}
type HostHashMap = HashMap<String, String>;
//STRUCTURE:
// /// Short description (without this they won't appear on the list)
// group {
@ -414,15 +428,15 @@ make_config! {
/// General settings
settings {
/// Domain URL |> This needs to be set to the URL used to access the server, including 'http[s]://'
/// and port, if it's different than the default. Some server functions don't work correctly without this value
domain: String, true, def, "http://localhost".to_string();
/// Comma seperated list of Domain URLs |> This needs to be set to the URL used to access the server, including
/// 'http[s]://' and port, if it's different than the default. Some server functions don't work correctly without this value
// TODO: Change back, this is only done to break existing references
domain_change_back: String, true, def, "http://localhost".to_string();
/// Domain Set |> Indicates if the domain is set by the admin. Otherwise the default will be used.
domain_set: bool, false, def, false;
/// Domain origin |> Domain URL origin (in https://example.com:8443/path, https://example.com:8443 is the origin)
domain_origin: String, false, auto, |c| extract_url_origin(&c.domain);
/// Domain path |> Domain URL path (in https://example.com:8443/path, /path is the path)
domain_path: String, false, auto, |c| extract_url_path(&c.domain);
/// MUST be the same for all domains.
domain_path: String, false, auto, |c| extract_url_path(&c.domain_change_back.split(',').nth(0).expect("Missing domain"));
/// Enable web vault
web_vault_enabled: bool, false, def, true;
@ -667,7 +681,7 @@ make_config! {
/// Embed images as email attachments.
smtp_embed_images: bool, true, def, true;
/// _smtp_img_src
_smtp_img_src: String, false, gen, |c| generate_smtp_img_src(c.smtp_embed_images, &c.domain);
_smtp_img_src: String, false, gen, |c| generate_smtp_img_src(c.smtp_embed_images, &c.domain_change_back);
/// Enable SMTP debugging (Know the risks!) |> DANGEROUS: Enabling this will output very detailed SMTP messages. This could contain sensitive information like passwords and usernames! Only enable this during troubleshooting!
smtp_debug: bool, false, def, false;
/// Accept Invalid Certs (Know the risks!) |> DANGEROUS: Allow invalid certificates. This option introduces significant vulnerabilities to man-in-the-middle attacks!
@ -1010,10 +1024,33 @@ fn extract_url_path(url: &str) -> String {
}
}
fn generate_smtp_img_src(embed_images: bool, domain: &str) -> String {
/// Extracts host part from a URL.
pub fn extract_url_host(url: &str) -> String {
match Url::parse(url) {
Ok(u) => {
let Some(mut host) = u.host_str().map(|s| s.to_string()) else {
println!("Domain does not contain host!");
return String::new();
};
if let Some(port) = u.port().map(|p| p.to_string()) {
host.push_str(&port);
}
host
}
Err(_) => {
// we already print it in the method above, no need to do it again
String::new()
}
}
}
fn generate_smtp_img_src(embed_images: bool, domains: &str) -> String {
if embed_images {
"cid:".to_string()
} else {
let domain = domains.split(',').nth(0).expect("Domain missing");
format!("{domain}/vw_static/")
}
}
@ -1082,6 +1119,9 @@ impl Config {
_env,
_usr,
_overrides,
domain_origins: OnceLock::new(),
domain_paths: OnceLock::new(),
domain_hostmap: OnceLock::new(),
}),
})
}
@ -1249,6 +1289,36 @@ impl Config {
}
}
}
pub fn domain_origin(&self, host: &str) -> Option<String> {
// This is done to prevent deadlock, when read-locking an rwlock twice
let domains = self.domain_change_back();
self.inner.read().unwrap().domain_origins.get_or_init(|| {
domains.split(',')
.map(|d| {
(extract_url_host(d), extract_url_origin(d))
})
.collect()
}).get(host).map(|h| h.clone())
}
pub fn host_to_domain(&self, host: &str) -> Option<String> {
// This is done to prevent deadlock, when read-locking an rwlock twice
let domains = self.domain_change_back();
self.inner.read().unwrap().domain_hostmap.get_or_init(|| {
domains.split(',')
.map(|d| {
(extract_url_host(d), extract_url_path(d))
})
.collect()
}).get(host).map(|h| h.clone())
}
pub fn main_domain(&self) -> String {
self.domain_change_back().split(',').nth(0).expect("Missing domain").to_string()
}
}
use handlebars::{