use std::path::{Path, PathBuf}; use clap::Parser; use crate::constants::{ APP_NAME, CLIENTS_LIST_FILE, OIDC_PROVIDER_CB_URI, PROVIDERS_LIST_FILE, USERS_LIST_FILE, }; /// Action logger format #[derive(Copy, Clone, Eq, PartialEq, Debug, clap::ValueEnum, Default)] pub enum ActionLoggerFormat { #[default] Text, Json, None, } /// 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, /// Overwrite clients list file path, if the file is not to be found in storage path #[clap(long, env)] pub clients_list_file_path: Option, /// Overwrite providers list file path, if the file is not to be found in storage path #[clap(long, env)] pub providers_list_file_path: Option, /// 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 /// /// Operating instance of IP location service : https://gitlab.com/pierre42100/iplocationserver /// /// Example: "https://api.geoip.rs" #[arg(long, short, env)] pub ip_location_service: Option, /// Action logger output format #[arg(long, env, default_value_t, value_enum)] pub action_logger_format: ActionLoggerFormat, /// Login background image #[arg(long, env, default_value = "/assets/img/forest.jpg")] pub login_background_image: String, /// Disable local login. If this option is set without any upstream providers set, it will be impossible /// to authenticate #[arg(long, env)] pub disable_local_login: bool, } 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 { match &self.clients_list_file_path { None => self.storage_path().join(CLIENTS_LIST_FILE), Some(p) => Path::new(p).to_path_buf(), } } pub fn providers_file(&self) -> PathBuf { match &self.providers_list_file_path { None => self.storage_path().join(PROVIDERS_LIST_FILE), Some(p) => Path::new(p).to_path_buf(), } } pub fn full_url(&self, uri: &str) -> String { if uri.starts_with('/') { format!("{}{}", self.website_origin, uri) } else { format!("{}/{}", self.website_origin, uri) } } /// Get the URL where a upstream OpenID provider should redirect /// the user after an authentication pub fn oidc_provider_redirect_url(&self) -> String { AppConfig::get().full_url(OIDC_PROVIDER_CB_URI) } pub fn domain_name(&self) -> &str { self.website_origin.split('/').nth(2).unwrap_or(APP_NAME) } /// Get the domain without the port pub fn domain_name_without_port(&self) -> &str { let domain = self.domain_name(); domain.split_once(':').map(|i| i.0).unwrap_or(domain) } } #[cfg(test)] mod test { use crate::data::app_config::AppConfig; #[test] fn verify_cli() { use clap::CommandFactory; AppConfig::command().debug_assert() } }