Settings hot reloading - Part 4

This commit is contained in:
mdecimus
2024-03-26 18:40:41 +01:00
parent d8af9b4576
commit 170ff38d1f
120 changed files with 2286 additions and 2594 deletions

View File

@@ -1,6 +1,6 @@
[package]
name = "directory"
version = "0.1.0"
version = "0.6.0"
edition = "2021"
resolver = "2"
@@ -37,3 +37,6 @@ serde = { version = "1.0", features = ["derive"]}
[dev-dependencies]
tokio = { version = "1.23", features = ["full"] }
[features]
test_mode = []

View File

@@ -33,22 +33,22 @@ use super::{ImapConnectionManager, ImapDirectory};
impl ImapDirectory {
pub fn from_config(config: &mut Config, prefix: impl AsKey) -> Option<Self> {
let prefix = prefix.as_key();
let address = config.value_require_((&prefix, "host"))?.to_string();
let address = config.value_require((&prefix, "host"))?.to_string();
let tls_implicit: bool = config
.property_or_default_((&prefix, "tls.enable"), "false")
.property_or_default((&prefix, "tls.enable"), "false")
.unwrap_or_default();
let port: u16 = config
.property_or_default_((&prefix, "port"), if tls_implicit { "993" } else { "143" })
.property_or_default((&prefix, "port"), if tls_implicit { "993" } else { "143" })
.unwrap_or(if tls_implicit { 993 } else { 143 });
let manager = ImapConnectionManager {
addr: format!("{address}:{port}"),
timeout: config
.property_or_default_((&prefix, "timeout"), "30s")
.property_or_default((&prefix, "timeout"), "30s")
.unwrap_or_else(|| Duration::from_secs(30)),
tls_connector: build_tls_connector(
config
.property_or_default_((&prefix, "tls.allow-invalid-certs"), "false")
.property_or_default((&prefix, "tls.allow-invalid-certs"), "false")
.unwrap_or_default(),
),
tls_hostname: address.to_string(),

View File

@@ -37,7 +37,7 @@ impl LdapDirectory {
let bind_dn = if let Some(dn) = config.value((&prefix, "bind.dn")) {
Bind::new(
dn.to_string(),
config.value_require_((&prefix, "bind.secret"))?.to_string(),
config.value_require((&prefix, "bind.secret"))?.to_string(),
)
.into()
} else {
@@ -45,28 +45,28 @@ impl LdapDirectory {
};
let manager = LdapConnectionManager::new(
config.value_require_((&prefix, "url"))?.to_string(),
config.value_require((&prefix, "url"))?.to_string(),
LdapConnSettings::new()
.set_conn_timeout(
config
.property_or_default_((&prefix, "timeout"), "30s")
.property_or_default((&prefix, "timeout"), "30s")
.unwrap_or_else(|| Duration::from_secs(30)),
)
.set_starttls(
config
.property_or_default_((&prefix, "tls.enable"), "false")
.property_or_default((&prefix, "tls.enable"), "false")
.unwrap_or_default(),
)
.set_no_tls_verify(
config
.property_or_default_((&prefix, "tls.allow-invalid-certs"), "false")
.property_or_default((&prefix, "tls.allow-invalid-certs"), "false")
.unwrap_or_default(),
),
bind_dn,
);
let mut mappings = LdapMappings {
base_dn: config.value_require_((&prefix, "base-dn"))?.to_string(),
base_dn: config.value_require((&prefix, "base-dn"))?.to_string(),
filter_name: LdapFilter::from_config(config, (&prefix, "filter.name")),
filter_email: LdapFilter::from_config(config, (&prefix, "filter.email")),
filter_verify: LdapFilter::from_config(config, (&prefix, "filter.verify")),
@@ -121,7 +121,7 @@ impl LdapDirectory {
}
let auth_bind = if config
.property_or_default_::<bool>((&prefix, "bind.auth.enable"), "false")
.property_or_default::<bool>((&prefix, "bind.auth.enable"), "false")
.unwrap_or_default()
{
LdapFilter::from_config(config, (&prefix, "bind.auth.dn")).into()

View File

@@ -49,7 +49,7 @@ impl MemoryDirectory {
{
let lookup_id = lookup_id.as_str();
let name = config
.value_require_((prefix.as_str(), "principals", lookup_id, "name"))?
.value_require((prefix.as_str(), "principals", lookup_id, "name"))?
.to_string();
let typ = match config.value((prefix.as_str(), "principals", lookup_id, "class")) {
Some("individual") => Type::Individual,
@@ -147,7 +147,7 @@ impl MemoryDirectory {
.value((prefix.as_str(), "principals", lookup_id, "description"))
.map(|v| v.to_string()),
quota: config
.property_((prefix.as_str(), "principals", lookup_id, "quota"))
.property((prefix.as_str(), "principals", lookup_id, "quota"))
.unwrap_or(0),
member_of,
id,

View File

@@ -33,23 +33,23 @@ use super::{SmtpConnectionManager, SmtpDirectory};
impl SmtpDirectory {
pub fn from_config(config: &mut Config, prefix: impl AsKey, is_lmtp: bool) -> Option<Self> {
let prefix = prefix.as_key();
let address = config.value_require_((&prefix, "host"))?.to_string();
let address = config.value_require((&prefix, "host"))?.to_string();
let tls_implicit: bool = config
.property_or_default_((&prefix, "tls.enable"), "false")
.property_or_default((&prefix, "tls.enable"), "false")
.unwrap_or_default();
let port: u16 = config
.property_or_default_((&prefix, "port"), if tls_implicit { "465" } else { "25" })
.property_or_default((&prefix, "port"), if tls_implicit { "465" } else { "25" })
.unwrap_or(if tls_implicit { 465 } else { 25 });
let manager = SmtpConnectionManager {
builder: SmtpClientBuilder {
addr: format!("{address}:{port}"),
timeout: config
.property_or_default_((&prefix, "timeout"), "30s")
.property_or_default((&prefix, "timeout"), "30s")
.unwrap_or_else(|| Duration::from_secs(30)),
tls_connector: build_tls_connector(
config
.property_or_default_((&prefix, "tls.allow-invalid-certs"), "false")
.property_or_default((&prefix, "tls.allow-invalid-certs"), "false")
.unwrap_or_default(),
),
tls_hostname: address.to_string(),
@@ -63,10 +63,10 @@ impl SmtpDirectory {
say_ehlo: false,
},
max_rcpt: config
.property_or_default_((&prefix, "limits.rcpt"), "10")
.property_or_default((&prefix, "limits.rcpt"), "10")
.unwrap_or(10),
max_auth_errors: config
.property_or_default_((&prefix, "limits.auth-errors"), "3")
.property_or_default((&prefix, "limits.auth-errors"), "3")
.unwrap_or(10),
};

View File

@@ -34,7 +34,7 @@ impl SqlDirectory {
data_store: Store,
) -> Option<Self> {
let prefix = prefix.as_key();
let store_id = config.value_require_((&prefix, "store"))?.to_string();
let store_id = config.value_require((&prefix, "store"))?.to_string();
let store = if let Some(store) = stores.lookup_stores.get(&store_id) {
store.clone()
} else {

View File

@@ -47,12 +47,12 @@ pub struct LookupCache<T: Hash + Eq> {
impl CachedDirectory {
pub fn try_from_config(config: &mut Config, prefix: impl AsKey) -> Option<Self> {
let prefix = prefix.as_key();
let cached_entries = config.property_((&prefix, "cache.entries"))?;
let cached_entries = config.property((&prefix, "cache.entries"))?;
let cache_ttl_positive = config
.property_((&prefix, "cache.ttl.positive"))
.property((&prefix, "cache.ttl.positive"))
.unwrap_or(Duration::from_secs(86400));
let cache_ttl_negative = config
.property_((&prefix, "cache.ttl.positive"))
.property((&prefix, "cache.ttl.positive"))
.unwrap_or_else(|| Duration::from_secs(3600));
Some(CachedDirectory {

View File

@@ -58,18 +58,18 @@ impl Directories {
#[cfg(feature = "test_mode")]
{
if config
.property_or_default_::<bool>(("directory", id, "disable"), "false")
.property_or_default::<bool>(("directory", id, "disable"), "false")
.unwrap_or(false)
{
tracing::debug!("Skipping disabled directory {id:?}.");
continue;
}
}
let protocol = config.value_require_(("directory", id, "type")).unwrap();
let protocol = config.value_require(("directory", id, "type")).unwrap();
let prefix = ("directory", id);
let store = match protocol {
"internal" => Some(DirectoryInner::Internal(
if let Some(store_id) = config.value_require_(("directory", id, "store")) {
if let Some(store_id) = config.value_require(("directory", id, "store")) {
if let Some(data) = stores.stores.get(store_id) {
match data.clone().init().await {
Ok(data) => data,
@@ -128,99 +128,6 @@ impl Directories {
}
}
#[allow(async_fn_in_trait)]
pub trait ConfigDirectory {
async fn parse_directory(
&mut self,
stores: &Stores,
data_store: Store,
) -> utils::config::Result<Directories>;
}
impl ConfigDirectory for Config {
async fn parse_directory(
&mut self,
stores: &Stores,
data_store: Store,
) -> utils::config::Result<Directories> {
let mut config = Directories {
directories: AHashMap::new(),
};
for id in self
.sub_keys("directory", ".type")
.map(|s| s.to_string())
.collect::<Vec<_>>()
{
// Parse directory
let id = id.as_str();
if self.property_or_default::<bool>(("directory", id, "disable"), "false")? {
tracing::debug!("Skipping disabled directory {id:?}.");
continue;
}
let protocol = self.value_require(("directory", id, "type"))?;
let prefix = ("directory", id);
let store = match protocol {
"internal" => DirectoryInner::Internal(
stores
.stores
.get(self.value_require(("directory", id, "store"))?)
.cloned()
.ok_or_else(|| {
format!(
"Failed to find store {:?} for directory {:?}.",
self.value_require(("directory", id, "store")).unwrap(),
id
)
})?
.init()
.await
.map_err(|err| {
format!(
"Failed to initialize store {:?} for directory {:?}: {:?}.",
self.value_require(("directory", id, "store")).unwrap(),
id,
err
)
})?,
),
"ldap" => DirectoryInner::Ldap(
LdapDirectory::from_config(self, prefix, data_store.clone()).unwrap(),
),
"sql" => DirectoryInner::Sql(
SqlDirectory::from_config(self, prefix, stores, data_store.clone()).unwrap(),
),
"imap" => DirectoryInner::Imap(ImapDirectory::from_config(self, prefix).unwrap()),
"smtp" => {
DirectoryInner::Smtp(SmtpDirectory::from_config(self, prefix, false).unwrap())
}
"lmtp" => {
DirectoryInner::Smtp(SmtpDirectory::from_config(self, prefix, true).unwrap())
}
"memory" => DirectoryInner::Memory(
MemoryDirectory::from_config(self, prefix, data_store.clone())
.await
.unwrap(),
),
unknown => {
return Err(format!("Unknown directory type: {unknown:?}"));
}
};
// Build directory
let directory = Arc::new(Directory {
store,
cache: CachedDirectory::try_from_config(self, ("directory", id)),
});
// Add directory
config.directories.insert(id.to_string(), directory);
}
Ok(config)
}
}
pub(crate) fn build_pool<M: Manager>(
config: &mut Config,
prefix: &str,
@@ -230,17 +137,17 @@ pub(crate) fn build_pool<M: Manager>(
.runtime(Runtime::Tokio1)
.max_size(
config
.property_or_default_((prefix, "pool.max-connections"), "10")
.property_or_default((prefix, "pool.max-connections"), "10")
.unwrap_or(10),
)
.create_timeout(
config
.property_or_default_::<Duration>((prefix, "pool.timeout.create"), "30s")
.property_or_default::<Duration>((prefix, "pool.timeout.create"), "30s")
.unwrap_or_else(|| Duration::from_secs(30))
.into(),
)
.wait_timeout(config.property_or_default_((prefix, "pool.timeout.wait"), "30s"))
.recycle_timeout(config.property_or_default_((prefix, "pool.timeout.recycle"), "30s"))
.wait_timeout(config.property_or_default((prefix, "pool.timeout.wait"), "30s"))
.recycle_timeout(config.property_or_default((prefix, "pool.timeout.recycle"), "30s"))
.build()
.map_err(|err| {
format!(

View File

@@ -94,11 +94,9 @@ impl Directory {
DirectoryInner::Memory(store) => store.rcpt(email).await,
}?;
if result {
// Update cache
if let Some(cache) = &self.cache {
cache.set_rcpt(email, true);
}
// Update cache
if let Some(cache) = &self.cache {
cache.set_rcpt(email, result);
}
Ok(result)