1
0
Fork 1
Spiegel von https://github.com/dani-garcia/vaultwarden.git synchronisiert 2024-09-28 21:53:55 +02:00

add configuration support for multiple domains

Dieser Commit ist enthalten in:
BlockListed 2023-09-09 08:50:45 +02:00
Ursprung 79ce5b49bc
Commit 80d3c61cc2
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 2D204777C477B588

Datei anzeigen

@ -1,6 +1,7 @@
use std::env::consts::EXE_SUFFIX; use std::{env::consts::EXE_SUFFIX, collections::HashMap};
use std::process::exit; use std::process::exit;
use std::sync::RwLock; use std::sync::RwLock;
use std::sync::OnceLock;
use job_scheduler_ng::Schedule; use job_scheduler_ng::Schedule;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
@ -47,6 +48,10 @@ macro_rules! make_config {
_usr: ConfigBuilder, _usr: ConfigBuilder,
_overrides: Vec<String>, _overrides: Vec<String>,
domain_hostmap: OnceLock<HostHashMap>,
domain_origins: OnceLock<HostHashMap>,
domain_paths: OnceLock<HostHashMap>,
} }
#[derive(Clone, Default, Deserialize, Serialize)] #[derive(Clone, Default, Deserialize, Serialize)]
@ -135,13 +140,20 @@ macro_rules! make_config {
fn build(&self) -> ConfigItems { fn build(&self) -> ConfigItems {
let mut config = ConfigItems::default(); 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.$name = make_config!{ @build self.$name.clone(), &config, $none_action, $($default)? };
)+)+ )+)+
config.domain_set = _domain_set; 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.signups_domains_whitelist = config.signups_domains_whitelist.trim().to_lowercase();
config.org_creation_users = config.org_creation_users.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: //STRUCTURE:
// /// Short description (without this they won't appear on the list) // /// Short description (without this they won't appear on the list)
// group { // group {
@ -414,15 +428,15 @@ make_config! {
/// General settings /// General settings
settings { settings {
/// Domain URL |> This needs to be set to the URL used to access the server, including 'http[s]://' /// Comma seperated list of Domain URLs |> This needs to be set to the URL used to access the server, including
/// and port, if it's different than the default. Some server functions don't work correctly without this value /// '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(); // 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 |> Indicates if the domain is set by the admin. Otherwise the default will be used.
domain_set: bool, false, def, false; 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 |> 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 /// Enable web vault
web_vault_enabled: bool, false, def, true; web_vault_enabled: bool, false, def, true;
@ -667,7 +681,7 @@ make_config! {
/// Embed images as email attachments. /// Embed images as email attachments.
smtp_embed_images: bool, true, def, true; smtp_embed_images: bool, true, def, true;
/// _smtp_img_src /// _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! /// 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; 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! /// 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 { if embed_images {
"cid:".to_string() "cid:".to_string()
} else { } else {
let domain = domains.split(',').nth(0).expect("Domain missing");
format!("{domain}/vw_static/") format!("{domain}/vw_static/")
} }
} }
@ -1082,6 +1119,9 @@ impl Config {
_env, _env,
_usr, _usr,
_overrides, _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::{ use handlebars::{