use std::path::{Path, PathBuf}; use clap::{Parser, ValueEnum}; use crate::constants::{APP_NAME, CLIENTS_LIST_FILE, USERS_LIST_FILE}; #[derive(ValueEnum, Default, Debug, Clone, Eq, PartialEq)] pub enum UsersBackend { #[default] File, Ldap, } #[derive(Debug, Clone)] pub struct LdapConfiguration { server_uri: &'static str, bind_dn: &'static str, bind_password: &'static str, start_tls: bool, root_dn: &'static str, attr_map: &'static str, } /// Basic OIDC provider #[derive(Parser, Debug, Clone)] #[clap(author, version, about, long_about = None)] pub struct AppConfig { /// Listen address #[clap(short, long, env, default_value = "0.0.0.0:8000")] pub listen_address: String, /// Storage path #[clap(short, long, env)] pub storage_path: String, /// App token token #[clap(short, long, env, default_value = "")] pub token_key: String, /// Website origin #[clap(short, long, env, default_value = "http://localhost:8000")] pub website_origin: String, /// Proxy IP, might end with a star "*" #[clap(short, long, env)] pub proxy_ip: Option, /// IP location service API /// /// Up instance of IP location service : /// /// Example: "" #[clap(long, short, env)] pub ip_location_service: Option, /// Users source backend #[clap(value_enum, long, short, default_value = "file")] pub backend: UsersBackend, /// LDAP server URI #[clap( long, env, group = "ldap-server-uri", requires = "ldap-dn", requires = "ldap-password" )] pub ldap_server_uri: Option, /// LDAP user DN to bind as to perform searches #[clap(long, env, group = "ldap-dn", requires = "ldap-server-uri")] pub ldap_dn: Option, /// LDAP user DN to bind as to perform searches #[clap(long, env, group = "ldap-password", requires = "ldap-server-uri")] pub ldap_password: Option, // /// The LDAP user filter, using `{0}` as the username placeholder, e.g. `(|(cn={0})(mail={0}))`; // /// uses standard LDAP search syntax. Default: `(uid={0})`. // #[clap(long, env, default_value = "uid={0}")] // pub ldap_search_filter: String, /// Set this argument to enable LDAP StartTLS support. (disabled by default) #[clap(long, env, default_value_t = false)] pub ldap_start_tls: bool, /// The LDAP search root DN, e.g. `dc=my,dc=domain,dc=com;` supports multiple entries in a /// space-delimited list, e.g. `dc=users,dc=domain,dc=com dc=admins,dc=domain,dc=com.` #[clap(long, env, requires = "ldap-server-uri")] pub ldap_root_dn: Option, /// A mapping of user attributes to LDAP values #[clap( long, env, default_value = "first_name:givenName,last_name:sn,username:uid,email:mail" )] pub ldap_attr_map: String, } lazy_static::lazy_static! { static ref ARGS: AppConfig = { let mut config = AppConfig::parse(); // In debug mode only, use dummy token if cfg!(debug_assertions) && config.token_key.is_empty() { config.token_key = String::from_utf8_lossy(&[32; 64]).to_string(); } config }; } impl AppConfig { /// Get parsed command line arguments pub fn get() -> &'static AppConfig { &ARGS } pub fn secure_cookie(&self) -> bool { self.website_origin.starts_with("https:") } pub fn storage_path(&self) -> &Path { self.storage_path.as_ref() } pub fn users_file(&self) -> PathBuf { self.storage_path().join(USERS_LIST_FILE) } pub fn clients_file(&self) -> PathBuf { self.storage_path().join(CLIENTS_LIST_FILE) } pub fn full_url(&self, uri: &str) -> String { if uri.starts_with('/') { format!("{}{}", self.website_origin, uri) } else { format!("{}/{}", self.website_origin, uri) } } pub fn domain_name(&self) -> &str { self.website_origin.split('/').nth(2).unwrap_or(APP_NAME) } /// Get LDAP configuration, if available pub fn ldap_config(&'static self) -> LdapConfiguration { assert_eq!(self.backend, UsersBackend::Ldap); LdapConfiguration { server_uri: self.ldap_server_uri.as_deref().unwrap(), bind_dn: self.ldap_dn.as_deref().unwrap(), bind_password: self.ldap_password.as_deref().unwrap(), start_tls: self.ldap_start_tls, root_dn: self.ldap_root_dn.as_deref().unwrap(), attr_map: self.ldap_attr_map.as_str(), } } } #[cfg(test)] mod test { use crate::data::app_config::AppConfig; #[test] fn verify_cli() { use clap::CommandFactory; AppConfig::command().debug_assert() } }