mirror of
https://git.verdigado.com/NB-Public/simple-wkd.git
synced 2024-12-05 03:32:49 +01:00
Add emails
This commit is contained in:
parent
5945294815
commit
9c35f4adef
9 changed files with 149 additions and 41 deletions
48
Cargo.lock
generated
48
Cargo.lock
generated
|
@ -662,6 +662,22 @@ version = "1.8.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||
|
||||
[[package]]
|
||||
name = "email-encoding"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbfb21b9878cf7a348dcb8559109aabc0ec40d69924bd706fa5149846c4fef75"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "email_address"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2153bd83ebc09db15bcbdc3e2194d901804952e3dc96967e1cd3b0c5c32d112"
|
||||
|
||||
[[package]]
|
||||
name = "ena"
|
||||
version = "0.14.2"
|
||||
|
@ -825,8 +841,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
|
@ -1186,6 +1204,29 @@ version = "1.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "lettre"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76bd09637ae3ec7bd605b8e135e757980b3968430ff2b1a4a94fb7769e50166d"
|
||||
dependencies = [
|
||||
"base64 0.21.0",
|
||||
"email-encoding",
|
||||
"email_address",
|
||||
"fastrand",
|
||||
"futures-util",
|
||||
"hostname",
|
||||
"httpdate",
|
||||
"idna 0.3.0",
|
||||
"mime",
|
||||
"native-tls",
|
||||
"nom",
|
||||
"once_cell",
|
||||
"quoted_printable",
|
||||
"socket2",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.141"
|
||||
|
@ -1588,6 +1629,12 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quoted_printable"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a24039f627d8285853cc90dcddf8c1ebfaa91f834566948872b225b9a28ed1b6"
|
||||
|
||||
[[package]]
|
||||
name = "radix_trie"
|
||||
version = "0.2.1"
|
||||
|
@ -1965,6 +2012,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"actix-web",
|
||||
"chrono",
|
||||
"lettre",
|
||||
"once_cell",
|
||||
"rand 0.8.5",
|
||||
"sequoia-net",
|
||||
|
|
|
@ -8,6 +8,7 @@ edition = "2021"
|
|||
[dependencies]
|
||||
actix-web = "4.3.1"
|
||||
chrono = "0.4.24"
|
||||
lettre = "0.10.4"
|
||||
once_cell = "1.17.1"
|
||||
rand = "0.8.5"
|
||||
sequoia-net = "0.27.0"
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
variant = "Advanced"
|
||||
root_folder = "data"
|
||||
max_age = 900
|
||||
cleanup_interval = 21600
|
||||
port = 8080
|
||||
external_url = "http://localhost:8080"
|
||||
|
||||
[folder_structure]
|
||||
root_folder = "data"
|
||||
pending_folder = "pending"
|
||||
|
||||
[smtp_settings]
|
||||
[mail_settings]
|
||||
smtp_host = "mail.example.org"
|
||||
smtp_username = "keyservice"
|
||||
smtp_password = "verysecurepassword"
|
||||
smtp_port = 465
|
||||
smtp_tls = "Tls"
|
||||
mail_from = "key-submission@example.org"
|
||||
mail_subject = "Confirm this action"
|
|
@ -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…
Reference in a new issue