use std::str::FromStr; use clap::{builder::EnumValueParser, Parser}; use lettre::message::Mailbox; use serde::{Deserialize, Serialize}; use strum::{EnumString, IntoStaticStr}; use url::Url; use crate::infra::database_string::DatabaseUrl; // Can be deserialized from either a boolean or a string, to facilitate migration. #[derive(Copy, Clone, Debug, Serialize, Default, EnumString, IntoStaticStr)] #[strum(ascii_case_insensitive)] pub enum TrueFalseAlways { #[default] False, True, Always, } impl TrueFalseAlways { pub fn is_positive(&self) -> bool { matches!(self, TrueFalseAlways::True | TrueFalseAlways::Always) } pub fn is_yes(&self) -> bool { matches!(self, TrueFalseAlways::True) } } impl<'de> Deserialize<'de> for TrueFalseAlways { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { use serde::de::{self}; struct Visitor; impl<'de> serde::de::Visitor<'de> for Visitor { type Value = TrueFalseAlways; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("true, false or always") } fn visit_bool(self, value: bool) -> Result where E: de::Error, { if value { Ok(TrueFalseAlways::True) } else { Ok(TrueFalseAlways::False) } } fn visit_str(self, value: &str) -> Result where E: de::Error, { match TrueFalseAlways::from_str(value) { Ok(v) => Ok(v), Err(_) => Err(de::Error::unknown_variant( value, &["true", "false", "always"], )), } } } deserializer.deserialize_any(Visitor) } } /// lldap is a lightweight LDAP server #[derive(Debug, Parser, Clone)] #[clap(version, author)] pub struct CLIOpts { /// Export #[clap(subcommand)] pub command: Command, } #[allow(clippy::large_enum_variant)] #[derive(Debug, Parser, Clone)] pub enum Command { /// Export the GraphQL schema to *.graphql. #[clap(name = "export_graphql_schema")] ExportGraphQLSchema(ExportGraphQLSchemaOpts), /// Run the LDAP and GraphQL server. #[clap(name = "run")] Run(RunOpts), /// Test whether the LDAP and GraphQL server are responsive. #[clap(name = "healthcheck")] HealthCheck(RunOpts), /// Send a test email. #[clap(name = "send_test_email")] SendTestEmail(TestEmailOpts), /// Create database schema. #[clap(name = "create_schema")] CreateSchema(RunOpts), } #[derive(Debug, Parser, Clone)] pub struct GeneralConfigOpts { /// Change config file name. #[clap( short, long, default_value = "lldap_config.toml", env = "LLDAP_CONFIG_FILE" )] pub config_file: String, /// Set verbose logging. #[clap(short, long)] pub verbose: bool, } #[derive(Debug, Parser, Clone)] pub struct RunOpts { #[clap(flatten)] pub general_config: GeneralConfigOpts, /// Path to the file that contains the private server key. /// It will be created if it doesn't exist. /// Alternatively, you can set `server_key_seed`. If `server_key_seed` is given, /// `server_key_file` will be ignored. #[clap(long, env = "LLDAP_SERVER_KEY_FILE")] pub server_key_file: Option, /// Seed used to generate the private server key. /// Takes precedence over `server_key_file`. #[clap(long, env = "LLDAP_SERVER_KEY_SEED")] pub server_key_seed: Option, /// Change ldap host. Default: "0.0.0.0" #[clap(long, env = "LLDAP_LDAP_HOST")] pub ldap_host: Option, /// Change ldap port. Default: 3890 #[clap(long, env = "LLDAP_LDAP_PORT")] pub ldap_port: Option, /// Change HTTP API host. Default: "0.0.0.0" #[clap(long, env = "LLDAP_HTTP_HOST")] pub http_host: Option, /// Change HTTP API port. Default: 17170 #[clap(long, env = "LLDAP_HTTP_PORT")] pub http_port: Option, /// URL of the server, for password reset links. #[clap(long, env = "LLDAP_HTTP_URL")] pub http_url: Option, /// Database connection URL #[clap(short, long, env = "LLDAP_DATABASE_URL")] pub database_url: Option, /// Force admin password reset to the config value. /// If set to true, it will be a one-time reset that doesn't start the server. /// You can set it to "always" to force a reset every time the server starts. #[clap(long, env = "LLDAP_FORCE_LDAP_USER_PASS_RESET")] pub force_ldap_user_pass_reset: Option, /// Force update of the private key after a key change. #[clap(long, env = "LLDAP_FORCE_UPDATE_PRIVATE_KEY")] pub force_update_private_key: Option, #[clap(flatten)] pub smtp_opts: SmtpOpts, #[clap(flatten)] pub ldaps_opts: LdapsOpts, } #[derive(Debug, Parser, Clone)] pub struct TestEmailOpts { #[clap(flatten)] pub general_config: GeneralConfigOpts, /// Email address to send an email to. #[clap(long, env = "LLDAP_TEST_EMAIL_TO")] pub to: String, #[clap(flatten)] pub smtp_opts: SmtpOpts, } #[derive(Debug, Parser, Clone)] #[clap(next_help_heading = Some("LDAPS"))] pub struct LdapsOpts { /// Enable LDAPS. Default: false. #[clap(long, env = "LLDAP_LDAPS_OPTIONS__ENABLED")] pub ldaps_enabled: Option, /// Change ldap ssl port. Default: 6360 #[clap(long, env = "LLDAP_LDAPS_OPTIONS__PORT")] pub ldaps_port: Option, /// Ldaps certificate file. Default: cert.pem #[clap(long, env = "LLDAP_LDAPS_OPTIONS__CERT_FILE")] pub ldaps_cert_file: Option, /// Ldaps certificate key file. Default: key.pem #[clap(long, env = "LLDAP_LDAPS_OPTIONS__KEY_FILE")] pub ldaps_key_file: Option, } #[derive(Clone, Debug, Deserialize, Serialize, clap::ValueEnum)] #[serde(rename_all = "UPPERCASE")] #[clap(rename_all = "UPPERCASE")] pub enum SmtpEncryption { None, Tls, StartTls, } #[derive(Debug, Parser, Clone)] #[clap(next_help_heading = Some("SMTP"))] pub struct SmtpOpts { /// Enable password reset. #[clap(long, env = "LLDAP_SMTP_OPTIONS__ENABLE_PASSWORD_RESET")] pub smtp_enable_password_reset: Option, /// Sender email address. #[clap(long, env = "LLDAP_SMTP_OPTIONS__FROM")] pub smtp_from: Option, /// Reply-to email address. #[clap(long, env = "LLDAP_SMTP_OPTIONS__TO")] pub smtp_reply_to: Option, /// SMTP server. #[clap(long, env = "LLDAP_SMTP_OPTIONS__SERVER")] pub smtp_server: Option, /// SMTP port, 587 by default. #[clap(long, env = "LLDAP_SMTP_OPTIONS__PORT")] pub smtp_port: Option, /// SMTP user. #[clap(long, env = "LLDAP_SMTP_OPTIONS__USER")] pub smtp_user: Option, /// SMTP password. #[clap(long, env = "LLDAP_SMTP_OPTIONS__PASSWORD", hide_env_values = true)] pub smtp_password: Option, /// Whether TLS should be used to connect to SMTP. #[clap(long, env = "LLDAP_SMTP_OPTIONS__TLS_REQUIRED", hide = true)] pub smtp_tls_required: Option, #[clap(long, env = "LLDAP_SMTP_OPTIONS__SMTP_ENCRYPTION", value_parser = EnumValueParser::::new(), ignore_case = true)] pub smtp_encryption: Option, } #[derive(Debug, Parser, Clone)] pub struct ExportGraphQLSchemaOpts { /// Output to a file. If not specified, the config is printed to the standard output. #[clap(short, long)] pub output_file: Option, } pub fn init() -> CLIOpts { CLIOpts::parse() }