WIP refactor
This commit is contained in:
parent
2779837a37
commit
3ddbce4529
5 changed files with 178 additions and 130 deletions
|
@ -12,7 +12,7 @@ pub struct CompanionConfig {
|
||||||
pub imap: ImapConfig,
|
pub imap: ImapConfig,
|
||||||
|
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub users: LoginStaticUser,
|
pub users: LoginStaticConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
@ -26,7 +26,7 @@ pub struct ProviderConfig {
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
#[serde(tag = "user_driver")]
|
#[serde(tag = "user_driver")]
|
||||||
pub enum UserManagement {
|
pub enum UserManagement {
|
||||||
Static(LoginStaticUser),
|
Static(LoginStaticConfig),
|
||||||
Ldap(LoginLdapConfig),
|
Ldap(LoginLdapConfig),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,8 +42,8 @@ pub struct ImapConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct LoginStaticUser {
|
pub struct LoginStaticConfig {
|
||||||
pub user_list: String,
|
pub user_list: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
@ -107,21 +107,40 @@ pub struct StaticGarageConfig {
|
||||||
pub bucket: String,
|
pub bucket: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type UserList = HashMap<String, UserEntry>;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
#[serde(tag = "crypto_root")]
|
||||||
|
pub enum CryptographyRoot {
|
||||||
|
PasswordProtected,
|
||||||
|
Keyring,
|
||||||
|
InPlace {
|
||||||
|
master_key: String,
|
||||||
|
secret_key: String,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct UserEntry {
|
pub struct UserEntry {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub email_addresses: Vec<String>,
|
pub email_addresses: Vec<String>,
|
||||||
pub password: String,
|
pub password: String,
|
||||||
|
|
||||||
pub master_key: Option<String>,
|
pub crypto_root: CryptographyRoot,
|
||||||
pub secret_key: Option<String>,
|
|
||||||
|
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub storage: StaticStorage,
|
pub storage: StaticStorage,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
#[serde(tag = "role")]
|
||||||
|
pub enum AnyConfig {
|
||||||
|
Companion(CompanionConfig),
|
||||||
|
Provider(ProviderConfig),
|
||||||
|
}
|
||||||
|
|
||||||
// ---
|
// ---
|
||||||
pub fn read_config(config_file: PathBuf) -> Result<Config> {
|
pub fn read_config<'a, T: Deserialize<'a>>(config_file: PathBuf) -> Result<T> {
|
||||||
let mut file = std::fs::OpenOptions::new()
|
let mut file = std::fs::OpenOptions::new()
|
||||||
.read(true)
|
.read(true)
|
||||||
.open(config_file.as_path())?;
|
.open(config_file.as_path())?;
|
||||||
|
|
|
@ -45,6 +45,7 @@ pub struct PublicCredentials {
|
||||||
pub public_key: PublicKey,
|
pub public_key: PublicKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
/// The struct UserSecrets represents intermediary secrets that are mixed in with the user's
|
/// The struct UserSecrets represents intermediary secrets that are mixed in with the user's
|
||||||
/// password when decrypting the cryptographic keys that are stored in their bucket.
|
/// password when decrypting the cryptographic keys that are stored in their bucket.
|
||||||
/// These secrets should be stored somewhere else (e.g. in the LDAP server or in the
|
/// These secrets should be stored somewhere else (e.g. in the LDAP server or in the
|
||||||
|
@ -57,6 +58,7 @@ pub struct UserSecrets {
|
||||||
/// with old passwords
|
/// with old passwords
|
||||||
pub alternate_user_secrets: Vec<String>,
|
pub alternate_user_secrets: Vec<String>,
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
/// The struct CryptoKeys contains the cryptographic keys used to encrypt and decrypt
|
/// The struct CryptoKeys contains the cryptographic keys used to encrypt and decrypt
|
||||||
/// data in a user's mailbox.
|
/// data in a user's mailbox.
|
||||||
|
@ -85,7 +87,6 @@ impl Credentials {
|
||||||
impl CryptoKeys {
|
impl CryptoKeys {
|
||||||
pub async fn init(
|
pub async fn init(
|
||||||
storage: &Builders,
|
storage: &Builders,
|
||||||
user_secrets: &UserSecrets,
|
|
||||||
password: &str,
|
password: &str,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
// Check that salt and public don't exist already
|
// Check that salt and public don't exist already
|
||||||
|
@ -113,7 +114,7 @@ impl CryptoKeys {
|
||||||
thread_rng().fill(&mut kdf_salt);
|
thread_rng().fill(&mut kdf_salt);
|
||||||
|
|
||||||
// Calculate key for password secret box
|
// Calculate key for password secret box
|
||||||
let password_key = user_secrets.derive_password_key(&kdf_salt, password)?;
|
let password_key = derive_password_key(&kdf_salt, password)?;
|
||||||
|
|
||||||
// Seal a secret box that contains our crypto keys
|
// Seal a secret box that contains our crypto keys
|
||||||
let password_sealed = seal(&keys.serialize(), &password_key)?;
|
let password_sealed = seal(&keys.serialize(), &password_key)?;
|
||||||
|
@ -169,7 +170,6 @@ impl CryptoKeys {
|
||||||
|
|
||||||
pub async fn open(
|
pub async fn open(
|
||||||
storage: &Builders,
|
storage: &Builders,
|
||||||
user_secrets: &UserSecrets,
|
|
||||||
password: &str,
|
password: &str,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let k2v = storage.row_store()?;
|
let k2v = storage.row_store()?;
|
||||||
|
@ -200,8 +200,7 @@ impl CryptoKeys {
|
||||||
|
|
||||||
// Try to open blob
|
// Try to open blob
|
||||||
let kdf_salt = &password_blob[..32];
|
let kdf_salt = &password_blob[..32];
|
||||||
let password_openned =
|
let password_openned = try_open_encrypted_keys(kdf_salt, password, &password_blob[32..])?;
|
||||||
user_secrets.try_open_encrypted_keys(kdf_salt, password, &password_blob[32..])?;
|
|
||||||
|
|
||||||
let keys = Self::deserialize(&password_openned)?;
|
let keys = Self::deserialize(&password_openned)?;
|
||||||
if keys.public != expected_public {
|
if keys.public != expected_public {
|
||||||
|
@ -238,7 +237,6 @@ impl CryptoKeys {
|
||||||
pub async fn add_password(
|
pub async fn add_password(
|
||||||
&self,
|
&self,
|
||||||
storage: &Builders,
|
storage: &Builders,
|
||||||
user_secrets: &UserSecrets,
|
|
||||||
password: &str,
|
password: &str,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let k2v = storage.row_store()?;
|
let k2v = storage.row_store()?;
|
||||||
|
@ -252,7 +250,7 @@ impl CryptoKeys {
|
||||||
thread_rng().fill(&mut kdf_salt);
|
thread_rng().fill(&mut kdf_salt);
|
||||||
|
|
||||||
// Calculate key for password secret box
|
// Calculate key for password secret box
|
||||||
let password_key = user_secrets.derive_password_key(&kdf_salt, password)?;
|
let password_key = derive_password_key(&kdf_salt, password)?;
|
||||||
|
|
||||||
// Seal a secret box that contains our crypto keys
|
// Seal a secret box that contains our crypto keys
|
||||||
let password_sealed = seal(&self.serialize(), &password_key)?;
|
let password_sealed = seal(&self.serialize(), &password_key)?;
|
||||||
|
@ -418,32 +416,13 @@ impl CryptoKeys {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserSecrets {
|
fn derive_password_key(kdf_salt: &[u8], password: &str) -> Result<Key> {
|
||||||
fn derive_password_key_with(user_secret: &str, kdf_salt: &[u8], password: &str) -> Result<Key> {
|
Ok(Key::from_slice(&argon2_kdf(kdf_salt, password.as_bytes(), 32)?).unwrap())
|
||||||
let tmp = format!("{}\n\n{}", user_secret, password);
|
}
|
||||||
Ok(Key::from_slice(&argon2_kdf(kdf_salt, tmp.as_bytes(), 32)?).unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn derive_password_key(&self, kdf_salt: &[u8], password: &str) -> Result<Key> {
|
fn try_open_encrypted_keys(kdf_salt: &[u8], password: &str, encrypted_keys: &[u8]) -> Result<Vec<u8>> {
|
||||||
Self::derive_password_key_with(&self.user_secret, kdf_salt, password)
|
let password_key = derive_password_key(kdf_salt, password)?;
|
||||||
}
|
open(encrypted_keys, &password_key)
|
||||||
|
|
||||||
fn try_open_encrypted_keys(
|
|
||||||
&self,
|
|
||||||
kdf_salt: &[u8],
|
|
||||||
password: &str,
|
|
||||||
encrypted_keys: &[u8],
|
|
||||||
) -> Result<Vec<u8>> {
|
|
||||||
let secrets_to_try =
|
|
||||||
std::iter::once(&self.user_secret).chain(self.alternate_user_secrets.iter());
|
|
||||||
for user_secret in secrets_to_try {
|
|
||||||
let password_key = Self::derive_password_key_with(user_secret, kdf_salt, password)?;
|
|
||||||
if let Ok(res) = open(encrypted_keys, &password_key) {
|
|
||||||
return Ok(res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bail!("Unable to decrypt password blob.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- UTIL ----
|
// ---- UTIL ----
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
@ -10,13 +11,28 @@ use crate::login::*;
|
||||||
use crate::storage;
|
use crate::storage;
|
||||||
|
|
||||||
pub struct StaticLoginProvider {
|
pub struct StaticLoginProvider {
|
||||||
users: HashMap<String, Arc<LoginStaticUser>>,
|
user_list: PathBuf,
|
||||||
users_by_email: HashMap<String, Arc<LoginStaticUser>>,
|
users: HashMap<String, Arc<UserEntry>>,
|
||||||
|
users_by_email: HashMap<String, Arc<UserEntry>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StaticLoginProvider {
|
impl StaticLoginProvider {
|
||||||
pub fn new(config: LoginStaticConfig) -> Result<Self> {
|
pub fn new(config: LoginStaticConfig) -> Result<Self> {
|
||||||
let users = config
|
let mut lp = Self {
|
||||||
|
user_list: config.user_list,
|
||||||
|
users: HashMap::new(),
|
||||||
|
users_by_email: HashMap::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
lp.update_user_list();
|
||||||
|
|
||||||
|
Ok(lp)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_user_list(&mut self) -> Result<()> {
|
||||||
|
let ulist: UserList = read_config(self.user_list)?;
|
||||||
|
|
||||||
|
let users = ulist
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(k, v)| (k, Arc::new(v)))
|
.map(|(k, v)| (k, Arc::new(v)))
|
||||||
.collect::<HashMap<_, _>>();
|
.collect::<HashMap<_, _>>();
|
||||||
|
@ -29,11 +45,7 @@ impl StaticLoginProvider {
|
||||||
users_by_email.insert(m.clone(), u.clone());
|
users_by_email.insert(m.clone(), u.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
Ok(Self {
|
|
||||||
users,
|
|
||||||
users_by_email,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,24 +76,18 @@ impl LoginProvider for StaticLoginProvider {
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
let keys = match (&user.master_key, &user.secret_key) {
|
let keys = match user.crypto_root { /*(&user.master_key, &user.secret_key) {*/
|
||||||
(Some(m), Some(s)) => {
|
CryptographyRoot::InPlace { master_key: m, secret_key: s } => {
|
||||||
let master_key =
|
let master_key =
|
||||||
Key::from_slice(&base64::decode(m)?).ok_or(anyhow!("Invalid master key"))?;
|
Key::from_slice(&base64::decode(m)?).ok_or(anyhow!("Invalid master key"))?;
|
||||||
let secret_key = SecretKey::from_slice(&base64::decode(s)?)
|
let secret_key = SecretKey::from_slice(&base64::decode(s)?)
|
||||||
.ok_or(anyhow!("Invalid secret key"))?;
|
.ok_or(anyhow!("Invalid secret key"))?;
|
||||||
CryptoKeys::open_without_password(&storage, &master_key, &secret_key).await?
|
CryptoKeys::open_without_password(&storage, &master_key, &secret_key).await?
|
||||||
}
|
}
|
||||||
(None, None) => {
|
CryptographyRoot::PasswordProtected => {
|
||||||
let user_secrets = UserSecrets {
|
CryptoKeys::open(&storage, password).await?
|
||||||
user_secret: user.user_secret.clone(),
|
|
||||||
alternate_user_secrets: user.alternate_user_secrets.clone(),
|
|
||||||
};
|
|
||||||
CryptoKeys::open(&storage, &user_secrets, password).await?
|
|
||||||
}
|
}
|
||||||
_ => bail!(
|
CryptographyRoot::Keyring => unimplemented!(),
|
||||||
"Either both master and secret key or none of them must be specified for user"
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
tracing::debug!(user=%username, "logged");
|
tracing::debug!(user=%username, "logged");
|
||||||
|
|
148
src/main.rs
148
src/main.rs
|
@ -25,26 +25,59 @@ use server::Server;
|
||||||
struct Args {
|
struct Args {
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
command: Command,
|
command: Command,
|
||||||
|
|
||||||
|
#[clap(short, long, env = "CONFIG_FILE", default_value = "aerogramme.toml")]
|
||||||
|
config_file: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
enum Command {
|
enum Command {
|
||||||
/// Runs the IMAP+LMTP server daemon
|
#[clap(subcommand)]
|
||||||
Server {
|
Companion(CompanionCommand),
|
||||||
#[clap(short, long, env = "CONFIG_FILE", default_value = "aerogramme.toml")]
|
|
||||||
config_file: PathBuf,
|
#[clap(subcommand)]
|
||||||
},
|
Provider(ProviderCommand),
|
||||||
Test,
|
//Test,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
struct UserSecretsArgs {
|
enum CompanionCommand {
|
||||||
/// User secret
|
/// Runs the IMAP proxy
|
||||||
#[clap(short = 'U', long, env = "USER_SECRET")]
|
Daemon,
|
||||||
user_secret: String,
|
Reload {
|
||||||
/// Alternate user secrets (comma-separated list of strings)
|
#[clap(short, long, env = "AEROGRAMME_PID")]
|
||||||
#[clap(long, env = "ALTERNATE_USER_SECRETS", default_value = "")]
|
pid: Option<u64>,
|
||||||
alternate_user_secrets: String,
|
},
|
||||||
|
Wizard,
|
||||||
|
#[clap(subcommand)]
|
||||||
|
Account(AccountManagement),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand, Debug)]
|
||||||
|
enum ProviderCommand {
|
||||||
|
/// Runs the IMAP+LMTP server daemon
|
||||||
|
Daemon,
|
||||||
|
Reload,
|
||||||
|
#[clap(subcommand)]
|
||||||
|
Account(AccountManagement),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand, Debug)]
|
||||||
|
enum AccountManagement {
|
||||||
|
Add {
|
||||||
|
#[clap(short, long)]
|
||||||
|
login: String,
|
||||||
|
#[clap(short, long)]
|
||||||
|
setup: PathBuf,
|
||||||
|
},
|
||||||
|
Delete {
|
||||||
|
#[clap(short, long)]
|
||||||
|
login: String,
|
||||||
|
},
|
||||||
|
ChangePassword {
|
||||||
|
#[clap(short, long)]
|
||||||
|
login: String
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
@ -63,43 +96,62 @@ async fn main() -> Result<()> {
|
||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
let any_config = read_config(args.config_file)?;
|
||||||
|
|
||||||
match args.command {
|
match (args.command, any_config) {
|
||||||
Command::Server { config_file } => {
|
(Command::Companion(subcommand), AnyConfig::Companion(config)) => match subcommand {
|
||||||
let config = read_config(config_file)?;
|
CompanionCommand::Daemon => {
|
||||||
|
let server = Server::from_companion_config(config).await?;
|
||||||
let server = Server::new(config).await?;
|
server.run().await?;
|
||||||
server.run().await?;
|
},
|
||||||
}
|
CompanionCommand::Reload { pid } => {
|
||||||
Command::Test => {
|
unimplemented!();
|
||||||
use std::collections::HashMap;
|
},
|
||||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
CompanionCommand::Wizard => {
|
||||||
println!("--- message pack ---\n{:?}\n--- end ---\n", rmp_serde::to_vec(&Config {
|
unimplemented!();
|
||||||
lmtp: None,
|
},
|
||||||
imap: Some(ImapConfig { bind_addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 8080) }),
|
CompanionCommand::Account(cmd) => {
|
||||||
login_ldap: None,
|
let user_file = config.users.user_list;
|
||||||
login_static: Some(HashMap::from([
|
account_management(cmd, user_file);
|
||||||
("alice".into(), LoginStaticUser {
|
}
|
||||||
password: "hash".into(),
|
},
|
||||||
user_secret: "hello".into(),
|
(Command::Provider(subcommand), AnyConfig::Provider(config)) => match subcommand {
|
||||||
alternate_user_secrets: vec![],
|
ProviderCommand::Daemon => {
|
||||||
email_addresses: vec![],
|
let server = Server::from_provider_config(config).await?;
|
||||||
master_key: None,
|
server.run().await?;
|
||||||
secret_key: None,
|
},
|
||||||
storage: StaticStorage::Garage(StaticGarageConfig {
|
ProviderCommand::Reload => {
|
||||||
s3_endpoint: "http://".into(),
|
unimplemented!();
|
||||||
k2v_endpoint: "http://".into(),
|
},
|
||||||
aws_region: "garage".into(),
|
ProviderCommand::Account(cmd) => {
|
||||||
aws_access_key_id: "GK...".into(),
|
let user_file = match config.users {
|
||||||
aws_secret_access_key: "xxx".into(),
|
UserManagement::Static(conf) => conf.user_list,
|
||||||
bucket: "aerogramme".into(),
|
UserManagement::Ldap(_) => panic!("LDAP account management is not supported from Aerogramme.")
|
||||||
}),
|
};
|
||||||
})
|
account_management(cmd, user_file);
|
||||||
])),
|
}
|
||||||
}).unwrap());
|
},
|
||||||
}
|
(Command::Provider(_), AnyConfig::Companion(_)) => {
|
||||||
|
panic!("Your want to run a 'Provider' command but your configuration file has role 'Companion'.");
|
||||||
|
},
|
||||||
|
(Command::Companion(_), AnyConfig::Provider(_)) => {
|
||||||
|
panic!("Your want to run a 'Companion' command but your configuration file has role 'Provider'.");
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn account_management(cmd: AccountManagement, users: PathBuf) {
|
||||||
|
match cmd {
|
||||||
|
Add => {
|
||||||
|
unimplemented!();
|
||||||
|
},
|
||||||
|
Delete => {
|
||||||
|
unimplemented!();
|
||||||
|
},
|
||||||
|
ChangePassword => {
|
||||||
|
unimplemented!();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::Result;
|
||||||
use futures::try_join;
|
use futures::try_join;
|
||||||
use log::*;
|
use log::*;
|
||||||
use tokio::sync::watch;
|
use tokio::sync::watch;
|
||||||
|
@ -17,19 +17,24 @@ pub struct Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
pub async fn new(config: Config) -> Result<Self> {
|
pub async fn from_companion_config(config: CompanionConfig) -> Result<Self> {
|
||||||
let (login, lmtp_conf, imap_conf) = build(config)?;
|
let login = Arc::new(StaticLoginProvider::new(config.users)?);
|
||||||
|
|
||||||
let lmtp_server = lmtp_conf.map(|cfg| LmtpServer::new(cfg, login.clone()));
|
let lmtp_server = None;
|
||||||
let imap_server = match imap_conf {
|
let imap_server = Some(imap::new(config.imap, login).await?);
|
||||||
Some(cfg) => Some(imap::new(cfg, login.clone()).await?),
|
Ok(Self { lmtp_server, imap_server })
|
||||||
None => None,
|
}
|
||||||
|
|
||||||
|
pub async fn from_provider_config(config: ProviderConfig) -> Result<Self> {
|
||||||
|
let login: ArcLoginProvider = match config.users {
|
||||||
|
UserManagement::Static(x) => Arc::new(StaticLoginProvider::new(x)?),
|
||||||
|
UserManagement::Ldap(x) => Arc::new(LdapLoginProvider::new(x)?),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Self {
|
let lmtp_server = Some(LmtpServer::new(config.lmtp, login.clone()));
|
||||||
lmtp_server,
|
let imap_server = Some(imap::new(config.imap, login).await?);
|
||||||
imap_server,
|
|
||||||
})
|
Ok(Self { lmtp_server, imap_server })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(self) -> Result<()> {
|
pub async fn run(self) -> Result<()> {
|
||||||
|
@ -60,19 +65,6 @@ impl Server {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build(config: Config) -> Result<(ArcLoginProvider, Option<LmtpConfig>, Option<ImapConfig>)> {
|
|
||||||
let lp: ArcLoginProvider = match (config.login_static, config.login_ldap) {
|
|
||||||
(Some(st), None) => Arc::new(StaticLoginProvider::new(st)?),
|
|
||||||
(None, Some(ld)) => Arc::new(LdapLoginProvider::new(ld)?),
|
|
||||||
(Some(_), Some(_)) => {
|
|
||||||
bail!("A single login provider must be set up in config file")
|
|
||||||
}
|
|
||||||
(None, None) => bail!("No login provider is set up in config file"),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((lp, config.lmtp, config.imap))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn watch_ctrl_c() -> (watch::Receiver<bool>, Arc<watch::Sender<bool>>) {
|
pub fn watch_ctrl_c() -> (watch::Receiver<bool>, Arc<watch::Sender<bool>>) {
|
||||||
let (send_cancel, watch_cancel) = watch::channel(false);
|
let (send_cancel, watch_cancel) = watch::channel(false);
|
||||||
let send_cancel = Arc::new(send_cancel);
|
let send_cancel = Arc::new(send_cancel);
|
||||||
|
|
Loading…
Reference in a new issue