mirror of
https://git.verdigado.com/NB-Public/simple-wkd.git
synced 2024-12-06 14:52:41 +01:00
Add emails
This commit is contained in:
parent
5945294815
commit
9c35f4adef
9 changed files with 149 additions and 41 deletions
|
@ -3,9 +3,12 @@ use chrono::Utc;
|
|||
use crate::errors::Error;
|
||||
use crate::management::{delete_key, Action, Pending};
|
||||
use crate::pending_path;
|
||||
use crate::settings::SETTINGS;
|
||||
use crate::settings::{SMTPEncryption, SETTINGS};
|
||||
use crate::utils::{get_email_from_cert, parse_pem};
|
||||
use crate::PENDING_FOLDER;
|
||||
|
||||
use lettre::transport::smtp::authentication::Credentials;
|
||||
use lettre::{Message, SmtpTransport, Transport};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
|
@ -37,7 +40,7 @@ pub fn confirm_action(token: &str) -> Result<(), Error> {
|
|||
None => return Err(Error::ParseEmail),
|
||||
};
|
||||
match sequoia_net::wkd::insert(
|
||||
&SETTINGS.folder_structure.root_folder,
|
||||
&SETTINGS.root_folder,
|
||||
domain,
|
||||
SETTINGS.variant,
|
||||
&cert,
|
||||
|
@ -55,8 +58,42 @@ pub fn confirm_action(token: &str) -> Result<(), Error> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn send_confirmation_email(email: &str, action: &Action, token: &str) {
|
||||
println!("Email sent to {email}");
|
||||
println!("Action: {action:?}");
|
||||
println!("Token: {token}");
|
||||
pub fn send_confirmation_email(email: &str, action: &Action, token: &str) -> Result<(), Error> {
|
||||
println!("Sending mail, token: {}", &token);
|
||||
let email = Message::builder()
|
||||
.from(match SETTINGS.mail_settings.mail_from.parse() {
|
||||
Ok(mailbox) => mailbox,
|
||||
Err(_) => panic!("Unable to parse the email in the settings!"),
|
||||
})
|
||||
.to(match email.parse() {
|
||||
Ok(mailbox) => mailbox,
|
||||
Err(_) => return Err(Error::ParseEmail),
|
||||
})
|
||||
.subject(&SETTINGS.mail_settings.mail_subject)
|
||||
.body(format!("{action} - {token}"));
|
||||
let message = match email {
|
||||
Ok(message) => message,
|
||||
Err(_) => return Err(Error::MailGeneration),
|
||||
};
|
||||
let creds = Credentials::new(
|
||||
SETTINGS.mail_settings.smtp_username.to_owned(),
|
||||
SETTINGS.mail_settings.smtp_password.to_owned(),
|
||||
);
|
||||
let builder = match &SETTINGS.mail_settings.smtp_tls {
|
||||
SMTPEncryption::Tls => SmtpTransport::relay(&SETTINGS.mail_settings.smtp_host),
|
||||
SMTPEncryption::Starttls => {
|
||||
SmtpTransport::starttls_relay(&SETTINGS.mail_settings.smtp_host)
|
||||
}
|
||||
};
|
||||
let mailer = match builder {
|
||||
Ok(builder) => builder,
|
||||
Err(_) => return Err(Error::SmtpBuilder),
|
||||
}
|
||||
.credentials(creds)
|
||||
.port(SETTINGS.mail_settings.smtp_port)
|
||||
.build();
|
||||
match mailer.send(&message) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err(Error::SendMail),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,26 +3,32 @@ use thiserror::Error;
|
|||
|
||||
#[derive(Error, Debug, Clone, Copy)]
|
||||
pub enum Error {
|
||||
#[error("Error while parsing cert")]
|
||||
#[error("EP1: Error while parsing cert")]
|
||||
ParseCert,
|
||||
#[error("Error while parsing an E-Mail address")]
|
||||
#[error("EP2: Error while parsing an E-Mail address")]
|
||||
ParseEmail,
|
||||
#[error("There is no pending request associated to this token")]
|
||||
#[error("EM1: There is no pending request associated to this token")]
|
||||
MissingPending,
|
||||
#[error("Requested key does not exist")]
|
||||
#[error("EM2: Requested key does not exist")]
|
||||
MissingKey,
|
||||
#[error("No E-Mail found in the certificate")]
|
||||
#[error("EM3: No E-Mail found in the certificate")]
|
||||
MissingMail,
|
||||
#[error("Error while serializing data")]
|
||||
#[error("EE1: Error while sending the E-Mail")]
|
||||
SendMail,
|
||||
#[error("EE2: Error while building the SMTP connection")]
|
||||
SmtpBuilder,
|
||||
#[error("ES1: rror while serializing data")]
|
||||
SerializeData,
|
||||
#[error("Error while deserializing data")]
|
||||
#[error("ES2: Error while deserializing data")]
|
||||
DeserializeData,
|
||||
#[error("The file is inaccessible")]
|
||||
#[error("ES3: The file is inaccessible")]
|
||||
Inaccessible,
|
||||
#[error("Error while adding a key to the wkd")]
|
||||
#[error("ES4: Error while adding a key to the wkd")]
|
||||
AddingKey,
|
||||
#[error("Error while generating the wkd path")]
|
||||
#[error("EG1: Error while generating the wkd path")]
|
||||
PathGeneration,
|
||||
#[error("EG2: Error while generating the email")]
|
||||
MailGeneration,
|
||||
}
|
||||
|
||||
impl actix_web::ResponseError for Error {
|
||||
|
|
10
src/main.rs
10
src/main.rs
|
@ -17,6 +17,8 @@ use std::fs;
|
|||
use std::path::Path;
|
||||
use tokio::{task, time};
|
||||
|
||||
const PENDING_FOLDER: &str = "pending";
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct Pem {
|
||||
key: String,
|
||||
|
@ -39,7 +41,9 @@ async fn main() -> std::io::Result<()> {
|
|||
let mut metronome = time::interval(time::Duration::from_secs(SETTINGS.cleanup_interval));
|
||||
loop {
|
||||
metronome.tick().await;
|
||||
clean_stale(SETTINGS.max_age).unwrap();
|
||||
if clean_stale(SETTINGS.max_age).is_err() {
|
||||
eprintln!("Error while cleaning stale requests...");
|
||||
}
|
||||
}
|
||||
});
|
||||
HttpServer::new(|| App::new().service(submit).service(confirm).service(delete))
|
||||
|
@ -54,7 +58,7 @@ async fn submit(pem: web::Form<Pem>) -> Result<String> {
|
|||
let email = get_email_from_cert(&cert)?;
|
||||
let token = gen_random_token();
|
||||
store_pending_addition(pem.key.clone(), &token)?;
|
||||
send_confirmation_email(&email, &Action::Add, &token);
|
||||
send_confirmation_email(&email, &Action::Add, &token)?;
|
||||
Ok(String::from("Key submitted successfully!"))
|
||||
}
|
||||
|
||||
|
@ -69,6 +73,6 @@ async fn delete(email: web::Path<Email>) -> Result<String> {
|
|||
key_exists(&email.address)?;
|
||||
let token = gen_random_token();
|
||||
store_pending_deletion(email.address.clone(), &token)?;
|
||||
send_confirmation_email(&email.address, &Action::Delete, &token);
|
||||
send_confirmation_email(&email.address, &Action::Delete, &token)?;
|
||||
Ok(String::from("Deletion request submitted successfully!"))
|
||||
}
|
||||
|
|
|
@ -2,10 +2,11 @@ use crate::errors::Error;
|
|||
use crate::pending_path;
|
||||
use crate::settings::SETTINGS;
|
||||
use crate::utils::get_user_file_path;
|
||||
use crate::PENDING_FOLDER;
|
||||
|
||||
use chrono::Utc;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fs, path::Path};
|
||||
use std::{fmt::Display, fs, path::Path};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub enum Action {
|
||||
|
@ -13,6 +14,12 @@ pub enum Action {
|
|||
Delete,
|
||||
}
|
||||
|
||||
impl Display for Action {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Pending {
|
||||
action: Action,
|
||||
|
@ -95,7 +102,7 @@ pub fn clean_stale(max_age: i64) -> Result<(), Error> {
|
|||
}
|
||||
|
||||
pub fn delete_key(email: &str) -> Result<(), Error> {
|
||||
let path = Path::new(&SETTINGS.folder_structure.root_folder).join(get_user_file_path(email)?);
|
||||
let path = Path::new(&SETTINGS.root_folder).join(get_user_file_path(email)?);
|
||||
match fs::remove_file(path) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err(Error::Inaccessible),
|
||||
|
|
|
@ -1,24 +1,18 @@
|
|||
use std::fs;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use sequoia_net::wkd::Variant;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Settings {
|
||||
#[serde(with = "VariantDef")]
|
||||
pub variant: Variant,
|
||||
pub root_folder: String,
|
||||
pub max_age: i64,
|
||||
pub cleanup_interval: u64,
|
||||
pub port: u16,
|
||||
pub folder_structure: FolderStructure,
|
||||
pub smtp_settings: MailSettings,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct FolderStructure {
|
||||
pub root_folder: String,
|
||||
pub pending_folder: String,
|
||||
pub external_url: String,
|
||||
pub mail_settings: MailSettings,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
@ -27,6 +21,7 @@ pub struct MailSettings {
|
|||
pub smtp_username: String,
|
||||
pub smtp_password: String,
|
||||
pub smtp_port: u16,
|
||||
pub smtp_tls: SMTPEncryption,
|
||||
pub mail_from: String,
|
||||
pub mail_subject: String,
|
||||
}
|
||||
|
@ -38,10 +33,22 @@ pub enum VariantDef {
|
|||
Direct,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub enum SMTPEncryption {
|
||||
Tls,
|
||||
Starttls,
|
||||
}
|
||||
|
||||
fn get_settings() -> Settings {
|
||||
println!("Reaing settings...");
|
||||
let content = fs::read_to_string("wkd.toml").unwrap();
|
||||
toml::from_str(&content).unwrap()
|
||||
let content = match fs::read_to_string("wkd.toml") {
|
||||
Ok(content) => content,
|
||||
Err(_) => panic!("Unable to access settings file!"),
|
||||
};
|
||||
match toml::from_str(&content) {
|
||||
Ok(settings) => settings,
|
||||
Err(_) => panic!("Unable to parse settings from file!"),
|
||||
}
|
||||
}
|
||||
|
||||
pub static SETTINGS: Lazy<Settings> = Lazy::new(get_settings);
|
||||
|
|
|
@ -9,8 +9,7 @@ use std::path::{Path, PathBuf};
|
|||
#[macro_export]
|
||||
macro_rules! pending_path {
|
||||
() => {
|
||||
Path::new(&SETTINGS.folder_structure.root_folder)
|
||||
.join(&SETTINGS.folder_structure.pending_folder)
|
||||
Path::new(&SETTINGS.root_folder).join(PENDING_FOLDER)
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -54,7 +53,7 @@ pub fn get_user_file_path(email: &str) -> Result<PathBuf, Error> {
|
|||
|
||||
pub fn key_exists(email: &str) -> Result<bool, Error> {
|
||||
let path = get_user_file_path(email)?;
|
||||
if !pending_path!().join(path).is_file() {
|
||||
if !Path::new(&SETTINGS.root_folder).join(path).is_file() {
|
||||
return Err(Error::MissingKey);
|
||||
}
|
||||
Ok(true)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue