From 14c7a96c282e20ff0d5343a7a378554f34983d21 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Thu, 23 Nov 2023 15:04:47 +0100 Subject: [PATCH] extract setup logic --- src/future_rest_admin_api.txt | 174 ++++++++++++++++++++++++ src/mail/user.rs | 4 +- src/main.rs | 249 +--------------------------------- src/server.rs | 2 +- 4 files changed, 179 insertions(+), 250 deletions(-) create mode 100644 src/future_rest_admin_api.txt diff --git a/src/future_rest_admin_api.txt b/src/future_rest_admin_api.txt new file mode 100644 index 0000000..19ece27 --- /dev/null +++ b/src/future_rest_admin_api.txt @@ -0,0 +1,174 @@ + Command::FirstLogin { + creds, + user_secrets, + } => { + let creds = make_storage_creds(creds); + let user_secrets = make_user_secrets(user_secrets); + + println!("Please enter your password for key decryption."); + println!("If you are using LDAP login, this must be your LDAP password."); + println!("If you are using the static login provider, enter any password, and this will also become your password for local IMAP access."); + let password = rpassword::prompt_password("Enter password: ")?; + let password_confirm = rpassword::prompt_password("Confirm password: ")?; + if password != password_confirm { + bail!("Passwords don't match."); + } + + CryptoKeys::init(&creds, &user_secrets, &password).await?; + + println!(""); + println!("Cryptographic key setup is complete."); + println!(""); + println!("If you are using the static login provider, add the following section to your .toml configuration file:"); + println!(""); + dump_config(&password, &creds); + } + Command::InitializeLocalKeys { creds } => { + let creds = make_storage_creds(creds); + + println!("Please enter a password for local IMAP access."); + println!("This password is not used for key decryption, your keys will be printed below (do not lose them!)"); + println!( + "If you plan on using LDAP login, stop right here and use `first-login` instead" + ); + let password = rpassword::prompt_password("Enter password: ")?; + let password_confirm = rpassword::prompt_password("Confirm password: ")?; + if password != password_confirm { + bail!("Passwords don't match."); + } + + let master = gen_key(); + let (_, secret) = gen_keypair(); + let keys = CryptoKeys::init_without_password(&creds, &master, &secret).await?; + + println!(""); + println!("Cryptographic key setup is complete."); + println!(""); + println!("Add the following section to your .toml configuration file:"); + println!(""); + dump_config(&password, &creds); + dump_keys(&keys); + } + Command::AddPassword { + creds, + user_secrets, + gen, + } => { + let creds = make_storage_creds(creds); + let user_secrets = make_user_secrets(user_secrets); + + let existing_password = + rpassword::prompt_password("Enter existing password to decrypt keys: ")?; + let new_password = if gen { + let password = base64::encode_config( + &u128::to_be_bytes(thread_rng().gen())[..10], + base64::URL_SAFE_NO_PAD, + ); + println!("Your new password: {}", password); + println!("Keep it safe!"); + password + } else { + let password = rpassword::prompt_password("Enter new password: ")?; + let password_confirm = rpassword::prompt_password("Confirm new password: ")?; + if password != password_confirm { + bail!("Passwords don't match."); + } + password + }; + + let keys = CryptoKeys::open(&creds, &user_secrets, &existing_password).await?; + keys.add_password(&creds, &user_secrets, &new_password) + .await?; + println!(""); + println!("New password added successfully."); + } + Command::DeletePassword { + creds, + user_secrets, + allow_delete_all, + } => { + let creds = make_storage_creds(creds); + let user_secrets = make_user_secrets(user_secrets); + + let existing_password = rpassword::prompt_password("Enter password to delete: ")?; + + let keys = match allow_delete_all { + true => Some(CryptoKeys::open(&creds, &user_secrets, &existing_password).await?), + false => None, + }; + + CryptoKeys::delete_password(&creds, &existing_password, allow_delete_all).await?; + + println!(""); + println!("Password was deleted successfully."); + + if let Some(keys) = keys { + println!("As a reminder, here are your cryptographic keys:"); + dump_keys(&keys); + } + } + Command::ShowKeys { + creds, + user_secrets, + } => { + let creds = make_storage_creds(creds); + let user_secrets = make_user_secrets(user_secrets); + + let existing_password = rpassword::prompt_password("Enter key decryption password: ")?; + + let keys = CryptoKeys::open(&creds, &user_secrets, &existing_password).await?; + dump_keys(&keys); + } + } + + Ok(()) +} + +fn make_storage_creds(c: StorageCredsArgs) -> StorageCredentials { + let s3_region = Region { + name: c.region.clone(), + endpoint: c.s3_endpoint, + }; + let k2v_region = Region { + name: c.region, + endpoint: c.k2v_endpoint, + }; + StorageCredentials { + k2v_region, + s3_region, + aws_access_key_id: c.aws_access_key_id, + aws_secret_access_key: c.aws_secret_access_key, + bucket: c.bucket, + } +} + +fn make_user_secrets(c: UserSecretsArgs) -> UserSecrets { + UserSecrets { + user_secret: c.user_secret, + alternate_user_secrets: c + .alternate_user_secrets + .split(',') + .map(|x| x.trim()) + .filter(|x| !x.is_empty()) + .map(|x| x.to_string()) + .collect(), + } +} + +fn dump_config(password: &str, creds: &StorageCredentials) { + println!("[login_static.users.]"); + println!( + "password = \"{}\"", + hash_password(password).expect("unable to hash password") + ); + println!("aws_access_key_id = \"{}\"", creds.aws_access_key_id); + println!( + "aws_secret_access_key = \"{}\"", + creds.aws_secret_access_key + ); +} + +fn dump_keys(keys: &CryptoKeys) { + println!("master_key = \"{}\"", base64::encode(&keys.master)); + println!("secret_key = \"{}\"", base64::encode(&keys.secret)); +} diff --git a/src/mail/user.rs b/src/mail/user.rs index 7a3e5c7..bdfb30c 100644 --- a/src/mail/user.rs +++ b/src/mail/user.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use tokio::sync::watch; use crate::cryptoblob::{open_deserialize, seal_serialize}; -use crate::login::{Credentials, StorageCredentials}; +use crate::login::Credentials; use crate::mail::incoming::incoming_mail_watch_process; use crate::mail::mailbox::Mailbox; use crate::mail::uidindex::ImapUidvalidity; @@ -309,7 +309,7 @@ impl User { Some(x) => self.k2v.from_orphan(x).expect("Source & target must be same storage"), None => self.k2v.row(MAILBOX_LIST_PK, MAILBOX_LIST_SK), }; - rref.set_value(list_blob).push().await?; + rref.set_value(&list_blob).push().await?; Ok(()) } } diff --git a/src/main.rs b/src/main.rs index f395143..9efd9a5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,13 +14,10 @@ mod storage; use std::path::PathBuf; -use anyhow::{bail, Result}; +use anyhow::Result; use clap::{Parser, Subcommand}; -use rand::prelude::*; use config::*; -use cryptoblob::*; -use login::{static_provider::*, *}; use server::Server; #[derive(Parser, Debug)] @@ -36,74 +33,7 @@ enum Command { Server { #[clap(short, long, env = "CONFIG_FILE", default_value = "aerogramme.toml")] config_file: PathBuf, - }, - /// TEST TEST TEST - Test { - #[clap(short, long, env = "CONFIG_FILE", default_value = "aerogramme.toml")] - config_file: PathBuf, - }, - /// Initializes key pairs for a user and adds a key decryption password - FirstLogin { - #[clap(flatten)] - creds: StorageCredsArgs, - #[clap(flatten)] - user_secrets: UserSecretsArgs, - }, - /// Initializes key pairs for a user and dumps keys to stdout for usage with static - /// login provider - InitializeLocalKeys { - #[clap(flatten)] - creds: StorageCredsArgs, - }, - /// Adds a key decryption password for a user - AddPassword { - #[clap(flatten)] - creds: StorageCredsArgs, - #[clap(flatten)] - user_secrets: UserSecretsArgs, - /// Automatically generate password - #[clap(short, long)] - gen: bool, - }, - /// Deletes a key decription password for a user - DeletePassword { - #[clap(flatten)] - creds: StorageCredsArgs, - #[clap(flatten)] - user_secrets: UserSecretsArgs, - /// Allow to delete all passwords - #[clap(long)] - allow_delete_all: bool, - }, - /// Dumps all encryption keys for user - ShowKeys { - #[clap(flatten)] - creds: StorageCredsArgs, - #[clap(flatten)] - user_secrets: UserSecretsArgs, - }, -} - -#[derive(Parser, Debug)] -struct StorageCredsArgs { - /// Name of the region to use - #[clap(short = 'r', long, env = "AWS_REGION")] - region: String, - /// Url of the endpoint to connect to for K2V - #[clap(short = 'k', long, env = "K2V_ENDPOINT")] - k2v_endpoint: String, - /// Url of the endpoint to connect to for S3 - #[clap(short = 's', long, env = "S3_ENDPOINT")] - s3_endpoint: String, - /// Access key ID - #[clap(short = 'A', long, env = "AWS_ACCESS_KEY_ID")] - aws_access_key_id: String, - /// Access key ID - #[clap(short = 'S', long, env = "AWS_SECRET_ACCESS_KEY")] - aws_secret_access_key: String, - /// Bucket name - #[clap(short = 'b', long, env = "BUCKET")] - bucket: String, + } } #[derive(Parser, Debug)] @@ -140,183 +70,8 @@ async fn main() -> Result<()> { let server = Server::new(config).await?; server.run().await?; } - Command::Test { config_file } => { - let config = read_config(config_file)?; - - let _server = Server::new(config).await?; - //server.test().await?; - } - Command::FirstLogin { - creds, - user_secrets, - } => { - let creds = make_storage_creds(creds); - let user_secrets = make_user_secrets(user_secrets); - - println!("Please enter your password for key decryption."); - println!("If you are using LDAP login, this must be your LDAP password."); - println!("If you are using the static login provider, enter any password, and this will also become your password for local IMAP access."); - let password = rpassword::prompt_password("Enter password: ")?; - let password_confirm = rpassword::prompt_password("Confirm password: ")?; - if password != password_confirm { - bail!("Passwords don't match."); - } - - CryptoKeys::init(&creds, &user_secrets, &password).await?; - - println!(""); - println!("Cryptographic key setup is complete."); - println!(""); - println!("If you are using the static login provider, add the following section to your .toml configuration file:"); - println!(""); - dump_config(&password, &creds); - } - Command::InitializeLocalKeys { creds } => { - let creds = make_storage_creds(creds); - - println!("Please enter a password for local IMAP access."); - println!("This password is not used for key decryption, your keys will be printed below (do not lose them!)"); - println!( - "If you plan on using LDAP login, stop right here and use `first-login` instead" - ); - let password = rpassword::prompt_password("Enter password: ")?; - let password_confirm = rpassword::prompt_password("Confirm password: ")?; - if password != password_confirm { - bail!("Passwords don't match."); - } - - let master = gen_key(); - let (_, secret) = gen_keypair(); - let keys = CryptoKeys::init_without_password(&creds, &master, &secret).await?; - - println!(""); - println!("Cryptographic key setup is complete."); - println!(""); - println!("Add the following section to your .toml configuration file:"); - println!(""); - dump_config(&password, &creds); - dump_keys(&keys); - } - Command::AddPassword { - creds, - user_secrets, - gen, - } => { - let creds = make_storage_creds(creds); - let user_secrets = make_user_secrets(user_secrets); - - let existing_password = - rpassword::prompt_password("Enter existing password to decrypt keys: ")?; - let new_password = if gen { - let password = base64::encode_config( - &u128::to_be_bytes(thread_rng().gen())[..10], - base64::URL_SAFE_NO_PAD, - ); - println!("Your new password: {}", password); - println!("Keep it safe!"); - password - } else { - let password = rpassword::prompt_password("Enter new password: ")?; - let password_confirm = rpassword::prompt_password("Confirm new password: ")?; - if password != password_confirm { - bail!("Passwords don't match."); - } - password - }; - - let keys = CryptoKeys::open(&creds, &user_secrets, &existing_password).await?; - keys.add_password(&creds, &user_secrets, &new_password) - .await?; - println!(""); - println!("New password added successfully."); - } - Command::DeletePassword { - creds, - user_secrets, - allow_delete_all, - } => { - let creds = make_storage_creds(creds); - let user_secrets = make_user_secrets(user_secrets); - - let existing_password = rpassword::prompt_password("Enter password to delete: ")?; - - let keys = match allow_delete_all { - true => Some(CryptoKeys::open(&creds, &user_secrets, &existing_password).await?), - false => None, - }; - - CryptoKeys::delete_password(&creds, &existing_password, allow_delete_all).await?; - - println!(""); - println!("Password was deleted successfully."); - - if let Some(keys) = keys { - println!("As a reminder, here are your cryptographic keys:"); - dump_keys(&keys); - } - } - Command::ShowKeys { - creds, - user_secrets, - } => { - let creds = make_storage_creds(creds); - let user_secrets = make_user_secrets(user_secrets); - - let existing_password = rpassword::prompt_password("Enter key decryption password: ")?; - - let keys = CryptoKeys::open(&creds, &user_secrets, &existing_password).await?; - dump_keys(&keys); - } } Ok(()) } -fn make_storage_creds(c: StorageCredsArgs) -> StorageCredentials { - let s3_region = Region { - name: c.region.clone(), - endpoint: c.s3_endpoint, - }; - let k2v_region = Region { - name: c.region, - endpoint: c.k2v_endpoint, - }; - StorageCredentials { - k2v_region, - s3_region, - aws_access_key_id: c.aws_access_key_id, - aws_secret_access_key: c.aws_secret_access_key, - bucket: c.bucket, - } -} - -fn make_user_secrets(c: UserSecretsArgs) -> UserSecrets { - UserSecrets { - user_secret: c.user_secret, - alternate_user_secrets: c - .alternate_user_secrets - .split(',') - .map(|x| x.trim()) - .filter(|x| !x.is_empty()) - .map(|x| x.to_string()) - .collect(), - } -} - -fn dump_config(password: &str, creds: &StorageCredentials) { - println!("[login_static.users.]"); - println!( - "password = \"{}\"", - hash_password(password).expect("unable to hash password") - ); - println!("aws_access_key_id = \"{}\"", creds.aws_access_key_id); - println!( - "aws_secret_access_key = \"{}\"", - creds.aws_secret_access_key - ); -} - -fn dump_keys(keys: &CryptoKeys) { - println!("master_key = \"{}\"", base64::encode(&keys.master)); - println!("secret_key = \"{}\"", base64::encode(&keys.secret)); -} diff --git a/src/server.rs b/src/server.rs index 3485a61..eca11ad 100644 --- a/src/server.rs +++ b/src/server.rs @@ -9,7 +9,7 @@ use crate::config::*; use crate::imap; use crate::lmtp::*; use crate::login::ArcLoginProvider; -use crate::login::{ldap_provider::*, static_provider::*, Region}; +use crate::login::{ldap_provider::*, static_provider::*}; pub struct Server { lmtp_server: Option>,