2021-11-11 09:14:03 +00:00
|
|
|
use crate::infra::cli::{GeneralConfigOpts, RunOpts, SmtpOpts, TestEmailOpts};
|
2021-08-26 19:56:42 +00:00
|
|
|
use anyhow::{Context, Result};
|
2021-03-02 19:30:43 +00:00
|
|
|
use figment::{
|
|
|
|
providers::{Env, Format, Serialized, Toml},
|
|
|
|
Figment,
|
|
|
|
};
|
2021-11-03 08:09:19 +00:00
|
|
|
use lettre::message::Mailbox;
|
2021-08-31 14:29:49 +00:00
|
|
|
use lldap_auth::opaque::{server::ServerSetup, KeyPair};
|
2021-03-02 19:30:43 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
2021-03-02 19:13:58 +00:00
|
|
|
|
2021-11-03 08:09:19 +00:00
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize, derive_builder::Builder)]
|
|
|
|
#[builder(pattern = "owned")]
|
|
|
|
pub struct MailOptions {
|
|
|
|
#[builder(default = "false")]
|
|
|
|
pub enable_password_reset: bool,
|
|
|
|
#[builder(default = "None")]
|
|
|
|
pub from: Option<Mailbox>,
|
|
|
|
#[builder(default = "None")]
|
|
|
|
pub reply_to: Option<Mailbox>,
|
|
|
|
#[builder(default = r#""localhost".to_string()"#)]
|
|
|
|
pub server: String,
|
|
|
|
#[builder(default = "587")]
|
|
|
|
pub port: u16,
|
|
|
|
#[builder(default = r#""admin".to_string()"#)]
|
|
|
|
pub user: String,
|
|
|
|
#[builder(default = r#""".to_string()"#)]
|
|
|
|
pub password: String,
|
|
|
|
#[builder(default = "true")]
|
|
|
|
pub tls_required: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::default::Default for MailOptions {
|
|
|
|
fn default() -> Self {
|
|
|
|
MailOptionsBuilder::default().build().unwrap()
|
|
|
|
}
|
|
|
|
}
|
2021-03-02 19:51:33 +00:00
|
|
|
|
2021-06-14 14:02:36 +00:00
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize, derive_builder::Builder)]
|
|
|
|
#[builder(
|
|
|
|
pattern = "owned",
|
|
|
|
build_fn(name = "private_build", validate = "Self::validate")
|
|
|
|
)]
|
2021-03-02 19:30:43 +00:00
|
|
|
pub struct Configuration {
|
2021-11-03 07:01:34 +00:00
|
|
|
#[builder(default = "3890")]
|
2021-03-02 22:07:01 +00:00
|
|
|
pub ldap_port: u16,
|
2021-11-03 07:01:34 +00:00
|
|
|
#[builder(default = "6360")]
|
2021-03-02 22:07:01 +00:00
|
|
|
pub ldaps_port: u16,
|
2021-11-03 07:01:34 +00:00
|
|
|
#[builder(default = "17170")]
|
2021-03-07 11:36:12 +00:00
|
|
|
pub http_port: u16,
|
2021-11-03 07:01:34 +00:00
|
|
|
#[builder(default = r#"String::from("secretjwtsecret")"#)]
|
2021-05-12 18:42:15 +00:00
|
|
|
pub jwt_secret: String,
|
2021-11-03 07:01:34 +00:00
|
|
|
#[builder(default = r#"String::from("dc=example,dc=com")"#)]
|
2021-03-16 17:27:31 +00:00
|
|
|
pub ldap_base_dn: String,
|
2021-11-03 07:01:34 +00:00
|
|
|
#[builder(default = r#"String::from("admin")"#)]
|
2021-03-12 08:33:43 +00:00
|
|
|
pub ldap_user_dn: String,
|
2021-11-03 07:01:34 +00:00
|
|
|
#[builder(default = r#"String::from("password")"#)]
|
2021-03-12 08:33:43 +00:00
|
|
|
pub ldap_user_pass: String,
|
2021-11-03 07:01:34 +00:00
|
|
|
#[builder(default = r#"String::from("sqlite://users.db?mode=rwc")"#)]
|
2021-03-12 08:33:43 +00:00
|
|
|
pub database_url: String,
|
2021-11-03 07:01:34 +00:00
|
|
|
#[builder(default = "false")]
|
2021-03-02 22:07:01 +00:00
|
|
|
pub verbose: bool,
|
2021-11-03 07:01:34 +00:00
|
|
|
#[builder(default = r#"String::from("server_key")"#)]
|
2021-06-14 14:02:36 +00:00
|
|
|
pub key_file: String,
|
2021-11-03 08:09:19 +00:00
|
|
|
#[builder(default)]
|
|
|
|
pub smtp_options: MailOptions,
|
2021-06-14 14:02:36 +00:00
|
|
|
#[serde(skip)]
|
|
|
|
#[builder(field(private), setter(strip_option))]
|
2021-06-23 18:33:36 +00:00
|
|
|
server_setup: Option<ServerSetup>,
|
2021-03-02 19:30:43 +00:00
|
|
|
}
|
|
|
|
|
2021-11-03 08:09:19 +00:00
|
|
|
impl std::default::Default for Configuration {
|
|
|
|
fn default() -> Self {
|
|
|
|
ConfigurationBuilder::default().build().unwrap()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-14 14:02:36 +00:00
|
|
|
impl ConfigurationBuilder {
|
|
|
|
pub fn build(self) -> Result<Configuration> {
|
2021-06-23 18:33:36 +00:00
|
|
|
let server_setup = get_server_setup(self.key_file.as_deref().unwrap_or("server_key"))?;
|
|
|
|
Ok(self.server_setup(server_setup).private_build()?)
|
2021-06-14 14:02:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn validate(&self) -> Result<(), String> {
|
2021-06-23 18:33:36 +00:00
|
|
|
if self.server_setup.is_none() {
|
2021-06-14 14:02:36 +00:00
|
|
|
Err("Don't use `private_build`, use `build` instead".to_string())
|
|
|
|
} else {
|
|
|
|
Ok(())
|
2021-03-02 19:30:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-02 20:43:26 +00:00
|
|
|
impl Configuration {
|
2021-06-23 18:33:36 +00:00
|
|
|
pub fn get_server_setup(&self) -> &ServerSetup {
|
|
|
|
self.server_setup.as_ref().unwrap()
|
|
|
|
}
|
|
|
|
|
2021-06-14 14:02:36 +00:00
|
|
|
pub fn get_server_keys(&self) -> &KeyPair {
|
2021-06-23 18:33:36 +00:00
|
|
|
self.get_server_setup().keypair()
|
2021-06-14 14:02:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-23 18:33:36 +00:00
|
|
|
fn get_server_setup(file_path: &str) -> Result<ServerSetup> {
|
2021-06-14 14:02:36 +00:00
|
|
|
use std::path::Path;
|
|
|
|
let path = Path::new(file_path);
|
|
|
|
if path.exists() {
|
2021-08-26 19:56:42 +00:00
|
|
|
let bytes =
|
|
|
|
std::fs::read(file_path).context(format!("Could not read key file `{}`", file_path))?;
|
2021-06-23 18:33:36 +00:00
|
|
|
Ok(ServerSetup::deserialize(&bytes)?)
|
2021-06-14 14:02:36 +00:00
|
|
|
} else {
|
|
|
|
let mut rng = rand::rngs::OsRng;
|
2021-06-23 18:33:36 +00:00
|
|
|
let server_setup = ServerSetup::new(&mut rng);
|
2021-08-26 19:56:42 +00:00
|
|
|
std::fs::write(path, server_setup.serialize()).context(format!(
|
|
|
|
"Could not write the generated server setup to file `{}`",
|
|
|
|
file_path,
|
|
|
|
))?;
|
2021-06-23 18:33:36 +00:00
|
|
|
Ok(server_setup)
|
2021-06-14 14:02:36 +00:00
|
|
|
}
|
2021-03-02 20:43:26 +00:00
|
|
|
}
|
|
|
|
|
2021-11-11 09:14:03 +00:00
|
|
|
pub trait ConfigOverrider {
|
|
|
|
fn override_config(&self, config: &mut Configuration);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait TopLevelCommandOpts {
|
|
|
|
fn general_config(&self) -> &GeneralConfigOpts;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TopLevelCommandOpts for RunOpts {
|
|
|
|
fn general_config(&self) -> &GeneralConfigOpts {
|
|
|
|
&self.general_config
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TopLevelCommandOpts for TestEmailOpts {
|
|
|
|
fn general_config(&self) -> &GeneralConfigOpts {
|
|
|
|
&self.general_config
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ConfigOverrider for RunOpts {
|
|
|
|
fn override_config(&self, config: &mut Configuration) {
|
|
|
|
self.general_config.override_config(config);
|
|
|
|
if let Some(port) = self.ldap_port {
|
|
|
|
config.ldap_port = port;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(port) = self.ldaps_port {
|
|
|
|
config.ldaps_port = port;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(port) = self.http_port {
|
|
|
|
config.http_port = port;
|
|
|
|
}
|
|
|
|
self.smtp_opts.override_config(config);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ConfigOverrider for TestEmailOpts {
|
|
|
|
fn override_config(&self, config: &mut Configuration) {
|
|
|
|
self.general_config.override_config(config);
|
|
|
|
self.smtp_opts.override_config(config);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ConfigOverrider for GeneralConfigOpts {
|
|
|
|
fn override_config(&self, config: &mut Configuration) {
|
|
|
|
if self.verbose {
|
|
|
|
config.verbose = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ConfigOverrider for SmtpOpts {
|
|
|
|
fn override_config(&self, config: &mut Configuration) {
|
|
|
|
if let Some(from) = &self.smtp_from {
|
|
|
|
config.smtp_options.from = Some(from.clone());
|
|
|
|
}
|
|
|
|
if let Some(reply_to) = &self.smtp_reply_to {
|
|
|
|
config.smtp_options.reply_to = Some(reply_to.clone());
|
|
|
|
}
|
|
|
|
if let Some(server) = &self.smtp_server {
|
|
|
|
config.smtp_options.server = server.clone();
|
|
|
|
}
|
|
|
|
if let Some(port) = self.smtp_port {
|
|
|
|
config.smtp_options.port = port;
|
|
|
|
}
|
|
|
|
if let Some(user) = &self.smtp_user {
|
|
|
|
config.smtp_options.user = user.clone();
|
|
|
|
}
|
|
|
|
if let Some(password) = &self.smtp_password {
|
|
|
|
config.smtp_options.password = password.clone();
|
|
|
|
}
|
|
|
|
if let Some(tls_required) = self.smtp_tls_required {
|
|
|
|
config.smtp_options.tls_required = tls_required;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn init<C>(overrides: C) -> Result<Configuration>
|
|
|
|
where
|
|
|
|
C: TopLevelCommandOpts + ConfigOverrider,
|
|
|
|
{
|
|
|
|
let config_file = overrides.general_config().config_file.clone();
|
2021-03-02 20:43:26 +00:00
|
|
|
|
2021-11-09 09:29:46 +00:00
|
|
|
println!(
|
|
|
|
"Loading configuration from {}",
|
2021-11-11 09:14:03 +00:00
|
|
|
overrides.general_config().config_file
|
2021-11-09 09:29:46 +00:00
|
|
|
);
|
2021-10-20 06:05:26 +00:00
|
|
|
|
2021-11-11 09:14:03 +00:00
|
|
|
let mut config: Configuration = Figment::from(Serialized::defaults(
|
2021-11-03 07:01:34 +00:00
|
|
|
ConfigurationBuilder::default().build().unwrap(),
|
|
|
|
))
|
|
|
|
.merge(Toml::file(config_file))
|
2021-11-03 08:09:19 +00:00
|
|
|
.merge(Env::prefixed("LLDAP_").split("__"))
|
2021-11-03 07:01:34 +00:00
|
|
|
.extract()?;
|
2021-03-02 19:30:43 +00:00
|
|
|
|
2021-11-11 09:14:03 +00:00
|
|
|
overrides.override_config(&mut config);
|
|
|
|
if config.verbose {
|
|
|
|
println!("Configuration: {:#?}", &config);
|
|
|
|
}
|
2021-06-23 18:33:36 +00:00
|
|
|
config.server_setup = Some(get_server_setup(&config.key_file)?);
|
2021-11-03 07:01:34 +00:00
|
|
|
if config.jwt_secret == "secretjwtsecret" {
|
|
|
|
println!("WARNING: Default JWT secret used! This is highly unsafe and can allow attackers to log in as admin.");
|
|
|
|
}
|
|
|
|
if config.ldap_user_pass == "password" {
|
|
|
|
println!("WARNING: Unsecure default admin password is used.");
|
|
|
|
}
|
2021-03-02 19:30:43 +00:00
|
|
|
Ok(config)
|
2021-03-02 19:13:58 +00:00
|
|
|
}
|