license and some functions

This commit is contained in:
Mateusz Gruszczyński committed 2026-07-29 14:03:46 +02:00
1 parent 0655cfe48c
commit ef8dfc67c1
56 files changed
+2586 -547

No files matched your search

+58 -22
View File
@@ -1,8 +1,17 @@
/*
* Copyright (C) 2026 Mateusz Gruszczyński @linuxiarz.pl
* Source-Available Code / Dual-Licensed.
*
* Free for non-commercial and evaluation use under terms of BSL/GPLv3.
* Commercial or production use requires a valid paid license.
* See LICENSE file in repository root for details.
*/
mod smtp;
mod values;
use std::{net::IpAddr, path::Path};
use smtp::load_smtp;
use std::{net::IpAddr, path::Path};
use values::ConfigValues;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -14,7 +23,12 @@ pub enum AuthorizationType {
impl AuthorizationType {
fn from_values(values: &ConfigValues) -> Result<Self, Box<dyn std::error::Error>> {
match values.get("AUTHORIZATION_TYPE", "local").trim().to_ascii_lowercase().as_str() {
match values
.get("AUTHORIZATION_TYPE", "local")
.trim()
.to_ascii_lowercase()
.as_str()
{
"local" => Ok(Self::Local),
"ldap" => Ok(Self::Ldap),
"ad" => Ok(Self::Ad),
@@ -63,13 +77,22 @@ impl Config {
let port = values.get("APP_PORT", "3000").parse()?;
let database_max_connections = values.get("DATABASE_MAX_CONNECTIONS", "8").parse()?;
let upload_max_size_mb: usize = values.get("UPLOAD_MAX_SIZE_MB", "20").parse()?;
let anonymous_access_token_ttl_days = values.positive_i64("ANONYMOUS_ACCESS_TOKEN_TTL_DAYS", 7)?;
let anonymous_access_token_ttl_days =
values.positive_i64("ANONYMOUS_ACCESS_TOKEN_TTL_DAYS", 7)?;
let user_session_ttl_days = values.positive_i64("USER_SESSION_TTL_DAYS", 3)?;
let unconfirmed_account_ttl_days = values.positive_i64("UNCONFIRMED_ACCOUNT_TTL_DAYS", 3)?;
let unconfirmed_account_ttl_days =
values.positive_i64("UNCONFIRMED_ACCOUNT_TTL_DAYS", 3)?;
let files_dir = values.get("FILES_DIR", "data/files");
let storage = match values.get("STORAGE_DRIVER", "local").trim().to_ascii_lowercase().as_str() {
"local" => crate::storage::StorageConfig::Local { root: files_dir.clone().into() },
let storage = match values
.get("STORAGE_DRIVER", "local")
.trim()
.to_ascii_lowercase()
.as_str()
{
"local" => crate::storage::StorageConfig::Local {
root: files_dir.clone().into(),
},
"s3" => crate::storage::StorageConfig::S3 {
endpoint: values.optional("S3_ENDPOINT"),
region: values.get("S3_REGION", "us-east-1"),
@@ -92,7 +115,10 @@ impl Config {
let context = format!("AUTHORIZATION_TYPE={}", authorization_type.as_str());
let (default_filter, default_username_attribute) = match authorization_type {
AuthorizationType::Ldap => ("(uid={username})", "uid"),
AuthorizationType::Ad => ("(|(sAMAccountName={username})(userPrincipalName={username}))", "sAMAccountName"),
AuthorizationType::Ad => (
"(|(sAMAccountName={username})(userPrincipalName={username}))",
"sAMAccountName",
),
AuthorizationType::Local => unreachable!(),
};
Some(crate::auth::ldap::LdapConfig {
@@ -102,21 +128,28 @@ impl Config {
bind_password: values.get("LDAP_BIND_PASSWORD", ""),
base_dn: values.required("LDAP_BASE_DN", &context)?,
user_filter: values.get("LDAP_USER_FILTER", default_filter),
username_attribute: values.get("LDAP_USERNAME_ATTRIBUTE", default_username_attribute),
username_attribute: values
.get("LDAP_USERNAME_ATTRIBUTE", default_username_attribute),
email_attribute: values.get("LDAP_EMAIL_ATTRIBUTE", "mail"),
display_name_attribute: values.get("LDAP_DISPLAY_NAME_ATTRIBUTE", "displayName"),
external_id_attribute: values.get("LDAP_EXTERNAL_ID_ATTRIBUTE", match authorization_type {
AuthorizationType::Ldap => "entryUUID",
AuthorizationType::Ad => "objectGUID",
AuthorizationType::Local => unreachable!(),
}),
display_name_attribute: values
.get("LDAP_DISPLAY_NAME_ATTRIBUTE", "displayName"),
external_id_attribute: values.get(
"LDAP_EXTERNAL_ID_ATTRIBUTE",
match authorization_type {
AuthorizationType::Ldap => "entryUUID",
AuthorizationType::Ad => "objectGUID",
AuthorizationType::Local => unreachable!(),
},
),
organization: values.get("LDAP_ORGANIZATION", "organization"),
provider: authorization_type.as_str().to_owned(),
email_required: values.bool("LDAP_EMAIL_REQUIRED", true)?,
link_existing_by_email: values.bool("LDAP_LINK_EXISTING_BY_EMAIL", false)?,
tls_verify: values.bool("LDAP_TLS_VERIFY", true)?,
connect_timeout_seconds: values.positive_u64("LDAP_CONNECT_TIMEOUT_SECONDS", 5)?,
operation_timeout_seconds: values.positive_u64("LDAP_OPERATION_TIMEOUT_SECONDS", 10)?,
connect_timeout_seconds: values
.positive_u64("LDAP_CONNECT_TIMEOUT_SECONDS", 5)?,
operation_timeout_seconds: values
.positive_u64("LDAP_OPERATION_TIMEOUT_SECONDS", 10)?,
})
}
};
@@ -131,10 +164,14 @@ impl Config {
static_dir: values.get("STATIC_DIR", "static"),
files_dir,
storage,
upload_max_size_bytes: upload_max_size_mb.checked_mul(1024 * 1024).ok_or("UPLOAD_MAX_SIZE_MB is too large")?,
upload_max_size_bytes: upload_max_size_mb
.checked_mul(1024 * 1024)
.ok_or("UPLOAD_MAX_SIZE_MB is too large")?,
asset_version: env!("CARGO_PKG_VERSION").to_owned(),
asset_cache_max_age_seconds: values.nonnegative_u64("ASSET_CACHE_MAX_AGE_SECONDS", 600)?,
file_cache_max_age_seconds: values.nonnegative_u64("FILE_CACHE_MAX_AGE_SECONDS", 600)?,
asset_cache_max_age_seconds: values
.nonnegative_u64("ASSET_CACHE_MAX_AGE_SECONDS", 600)?,
file_cache_max_age_seconds: values
.nonnegative_u64("FILE_CACHE_MAX_AGE_SECONDS", 600)?,
smtp,
registration_enabled: values.bool("REGISTRATION_ENABLED", false)?,
account_confirmation_required: values.bool("ACCOUNT_CONFIRMATION_REQUIRED", false)?,
@@ -165,12 +202,11 @@ impl Config {
return Err("STATIC_DIR and FILES_DIR cannot be empty".into());
}
if let Some(smtp) = &self.smtp {
if !(smtp.public_url.starts_with("http://") || smtp.public_url.starts_with("https://")) {
if !(smtp.public_url.starts_with("http://") || smtp.public_url.starts_with("https://"))
{
return Err("PUBLIC_URL must start with http:// or https://".into());
}
}
Ok(())
}
}