Settings hot reloading - Part 4
This commit is contained in:
@@ -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 = []
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user