0
0
Fork 0
mirror of https://git.verdigado.com/NB-Public/simple-wkd.git synced 2024-10-30 05:05:52 +01:00

Improve logger

This commit is contained in:
Delta1925 2023-04-14 18:55:17 +02:00
parent b717852376
commit 8484febb9d
No known key found for this signature in database
GPG key ID: 1C21ACE44193CB25
5 changed files with 154 additions and 43 deletions

View file

@ -1,42 +1,67 @@
use chrono::Utc; use chrono::Utc;
use log::{debug, error, trace, warn};
use crate::errors::Error; use crate::errors::Error;
use crate::management::{delete_key, Action, Pending}; use crate::management::{delete_key, Action, Pending};
use crate::pending_path; use crate::pending_path;
use crate::settings::{MAILER, SETTINGS}; use crate::settings::{MAILER, SETTINGS};
use crate::utils::{get_email_from_cert, parse_pem}; use crate::utils::{get_email_from_cert, get_filename, parse_pem};
use crate::PENDING_FOLDER; use crate::PENDING_FOLDER;
use lettre::{Message, Transport}; use lettre::{Message, Transport};
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
pub fn confirm_action(token: &str) -> Result<(), Error> { pub fn confirm_action(token: &str) -> Result<(Action, String), Error> {
trace!("Handling token {}", token);
let pending_path = pending_path!().join(token); let pending_path = pending_path!().join(token);
let content = if pending_path.is_file() { let content = if pending_path.is_file() {
match fs::read_to_string(&pending_path) { match fs::read_to_string(&pending_path) {
Ok(content) => content, Ok(content) => content,
Err(_) => return Err(Error::Inaccessible), Err(_) => {
warn!(
"Token {} was requested, but can't be read to string!",
token
);
return Err(Error::Inaccessible);
}
} }
} else { } else {
trace!("Requested token {} isn't a file", token);
return Err(Error::MissingPending); return Err(Error::MissingPending);
}; };
let key = match serde_json::from_str::<Pending>(&content) { let key = match serde_json::from_str::<Pending>(&content) {
Ok(key) => key, Ok(key) => key,
Err(_) => return Err(Error::DeserializeData), Err(_) => {
warn!("Error while deserializing token {}!", token);
return Err(Error::DeserializeData);
}
}; };
if Utc::now().timestamp() - key.timestamp() > SETTINGS.max_age { if Utc::now().timestamp() - key.timestamp() > SETTINGS.max_age {
match fs::remove_file(pending_path) { match fs::remove_file(&pending_path) {
Ok(_) => Err(Error::MissingPending), Ok(_) => {
Err(_) => Err(Error::Inaccessible), debug!(
"Deleted stale token {}",
get_filename(&pending_path).unwrap()
);
Err(Error::MissingPending)
}
Err(_) => {
warn!("Stale token {} can't be deleted!", token);
Err(Error::Inaccessible)
}
} }
} else { } else {
match key.action() { let address = match key.action() {
Action::Add => { Action::Add => {
let cert = parse_pem(key.data())?; let cert = parse_pem(key.data())?;
let domain = match get_email_from_cert(&cert)?.split('@').last() { let email = get_email_from_cert(&cert)?;
let domain = match email.split('@').last() {
Some(domain) => domain.to_string(), Some(domain) => domain.to_string(),
None => return Err(Error::ParseEmail), None => {
warn!("Error while parsing email's domain in token {}", token);
return Err(Error::ParseEmail);
}
}; };
match sequoia_net::wkd::insert( match sequoia_net::wkd::insert(
&SETTINGS.root_folder, &SETTINGS.root_folder,
@ -44,28 +69,54 @@ pub fn confirm_action(token: &str) -> Result<(), Error> {
SETTINGS.variant, SETTINGS.variant,
&cert, &cert,
) { ) {
Ok(_) => (), Ok(_) => email,
Err(_) => return Err(Error::AddingKey), Err(_) => {
warn!("Unable to create a wkd entry for token {}", token);
return Err(Error::AddingKey);
} }
} }
Action::Delete => delete_key(key.data())?,
} }
Action::Delete => match delete_key(key.data()) {
Ok(_) => key.data().to_owned(),
Err(error) => {
warn!("Unable to delete key for user {}", key.data());
return Err(error);
}
},
};
debug!("Token {} was confirmed", token);
match fs::remove_file(&pending_path) { match fs::remove_file(&pending_path) {
Ok(_) => Ok(()), Ok(_) => {
Err(_) => Err(Error::Inaccessible), trace!(
"Deleted confirmed token {}",
pending_path.file_name().unwrap().to_str().unwrap()
);
Ok((*key.action(), address))
}
Err(_) => {
warn!("Unable to delete confirmed token {}", token);
Err(Error::Inaccessible)
}
} }
} }
} }
pub fn send_confirmation_email(email: &str, action: &Action, token: &str) -> Result<(), Error> { pub fn send_confirmation_email(address: &str, action: &Action, token: &str) -> Result<(), Error> {
debug!("Sending email to {}", address);
let email = Message::builder() let email = Message::builder()
.from(match SETTINGS.mail_settings.mail_from.parse() { .from(match SETTINGS.mail_settings.mail_from.parse() {
Ok(mailbox) => mailbox, Ok(mailbox) => mailbox,
Err(_) => panic!("Unable to parse the email in the settings!"), Err(_) => {
error!("Unable to parse the email in the settings!");
panic!("Unable to parse the email in the settings!")
}
}) })
.to(match email.parse() { .to(match address.parse() {
Ok(mailbox) => mailbox, Ok(mailbox) => mailbox,
Err(_) => return Err(Error::ParseEmail), Err(_) => {
warn!("Error while parsing destination email for token {}", token);
return Err(Error::ParseEmail);
}
}) })
.subject( .subject(
SETTINGS SETTINGS
@ -87,11 +138,20 @@ pub fn send_confirmation_email(email: &str, action: &Action, token: &str) -> Res
let message = match email { let message = match email {
Ok(message) => message, Ok(message) => message,
Err(_) => return Err(Error::MailGeneration), Err(_) => {
warn!("Unable to build email for token {}", token);
return Err(Error::MailGeneration);
}
}; };
match MAILER.send(&message) { match MAILER.send(&message) {
Ok(_) => Ok(()), Ok(_) => {
Err(_) => Err(Error::SendMail), debug!("successfully sent email to {}", address);
Ok(())
}
Err(_) => {
warn!("Unable to send email to {}", address);
Err(Error::SendMail)
}
} }
} }

View file

@ -12,7 +12,7 @@ use self::management::{clean_stale, store_pending_addition, store_pending_deleti
use self::utils::{gen_random_token, get_email_from_cert, parse_pem}; use self::utils::{gen_random_token, get_email_from_cert, parse_pem};
use actix_web::{get, post, web, App, HttpServer, Result}; use actix_web::{get, post, web, App, HttpServer, Result};
use log::error; use log::{error, info};
use serde::Deserialize; use serde::Deserialize;
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
@ -39,6 +39,7 @@ struct Email {
#[actix_web::main] #[actix_web::main]
async fn main() -> std::io::Result<()> { async fn main() -> std::io::Result<()> {
if init_logger().is_err() { if init_logger().is_err() {
error!("Could not set up logger!");
panic!("Could not set up logger!") panic!("Could not set up logger!")
}; };
fs::create_dir_all(pending_path!())?; fs::create_dir_all(pending_path!())?;
@ -46,9 +47,9 @@ async fn main() -> std::io::Result<()> {
let mut metronome = time::interval(time::Duration::from_secs(SETTINGS.cleanup_interval)); let mut metronome = time::interval(time::Duration::from_secs(SETTINGS.cleanup_interval));
loop { loop {
metronome.tick().await; metronome.tick().await;
if clean_stale(SETTINGS.max_age).is_err() { info!("Running cleanup...");
error!("Error while cleaning stale requests!"); clean_stale(SETTINGS.max_age);
} info!("Cleanup completed!");
} }
}); });
HttpServer::new(|| App::new().service(submit).service(confirm).service(delete)) HttpServer::new(|| App::new().service(submit).service(confirm).service(delete))
@ -62,14 +63,19 @@ async fn submit(pem: web::Form<Pem>) -> Result<String> {
let cert = parse_pem(&pem.key)?; let cert = parse_pem(&pem.key)?;
let email = get_email_from_cert(&cert)?; let email = get_email_from_cert(&cert)?;
let token = gen_random_token(); let token = gen_random_token();
store_pending_addition(pem.key.clone(), &token)?; store_pending_addition(pem.key.clone(), &email, &token)?;
send_confirmation_email(&email, &Action::Add, &token)?; send_confirmation_email(&email, &Action::Add, &token)?;
info!("User {} submitted a key!", &email);
Ok(String::from("Key submitted successfully!")) Ok(String::from("Key submitted successfully!"))
} }
#[get("/api/confirm/{value}")] #[get("/api/confirm/{value}")]
async fn confirm(token: web::Path<Token>) -> Result<String> { async fn confirm(token: web::Path<Token>) -> Result<String> {
confirm_action(&token.value)?; let (action, email) = confirm_action(&token.value)?;
match action {
Action::Add => info!("Key for user {} was added successfully!", email),
Action::Delete => info!("Key for user {} was deleted successfully!", email),
}
Ok(String::from("Confirmation successful!")) Ok(String::from("Confirmation successful!"))
} }
@ -79,5 +85,6 @@ async fn delete(email: web::Path<Email>) -> Result<String> {
let token = gen_random_token(); let token = gen_random_token();
store_pending_deletion(email.address.clone(), &token)?; store_pending_deletion(email.address.clone(), &token)?;
send_confirmation_email(&email.address, &Action::Delete, &token)?; send_confirmation_email(&email.address, &Action::Delete, &token)?;
info!("User {} requested the deletion of his key!", email.address);
Ok(String::from("Deletion request submitted successfully!")) Ok(String::from("Deletion request submitted successfully!"))
} }

View file

@ -1,14 +1,15 @@
use crate::errors::Error;
use crate::pending_path; use crate::pending_path;
use crate::settings::SETTINGS; use crate::settings::SETTINGS;
use crate::utils::get_user_file_path; use crate::utils::get_user_file_path;
use crate::PENDING_FOLDER; use crate::PENDING_FOLDER;
use crate::{errors::Error, utils::get_filename};
use chrono::Utc; use chrono::Utc;
use log::{debug, warn};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{fmt::Display, fs, path::Path}; use std::{fmt::Display, fs, path::Path};
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub enum Action { pub enum Action {
Add, Add,
Delete, Delete,
@ -65,40 +66,62 @@ fn store_pending(pending: &Pending, token: &str) -> Result<(), Error> {
} }
} }
pub fn store_pending_addition(pem: String, token: &str) -> Result<(), Error> { pub fn store_pending_addition(pem: String, email: &str, token: &str) -> Result<(), Error> {
let pending = Pending::build_add(pem); let pending = Pending::build_add(pem);
store_pending(&pending, token)?; store_pending(&pending, token)?;
debug!("Stored submission from {} with token {}", email, token);
Ok(()) Ok(())
} }
pub fn store_pending_deletion(email: String, token: &str) -> Result<(), Error> { pub fn store_pending_deletion(email: String, token: &str) -> Result<(), Error> {
let pending = Pending::build_delete(email); let pending = Pending::build_delete(email.clone());
store_pending(&pending, token)?; store_pending(&pending, token)?;
debug!(
"Stored deletion request from {} with token {}",
email, token
);
Ok(()) Ok(())
} }
pub fn clean_stale(max_age: i64) -> Result<(), Error> { pub fn clean_stale(max_age: i64) {
for path in fs::read_dir(pending_path!()).unwrap().flatten() { for path in fs::read_dir(pending_path!()).unwrap().flatten() {
let file_path = path.path(); let file_path = path.path();
if file_path.is_file() { if file_path.is_file() {
let content = match fs::read_to_string(&file_path) { let content = match fs::read_to_string(&file_path) {
Ok(content) => content, Ok(content) => content,
Err(_) => return Err(Error::Inaccessible), Err(_) => {
warn!(
"Could not read contents of token {} to string",
get_filename(&file_path).unwrap()
);
continue;
}
}; };
let key = match serde_json::from_str::<Pending>(&content) { let key = match serde_json::from_str::<Pending>(&content) {
Ok(key) => key, Ok(key) => key,
Err(_) => return Err(Error::DeserializeData), Err(_) => {
warn!(
"Could not deserialize token {}",
get_filename(&file_path).unwrap()
);
continue;
}
}; };
let now = Utc::now().timestamp(); let now = Utc::now().timestamp();
if now - key.timestamp() > max_age { if now - key.timestamp() > max_age {
let err = fs::remove_file(&file_path).is_err(); if fs::remove_file(&file_path).is_err() {
if err { {
return Err(Error::Inaccessible); warn!(
"Could not delete stale token {}",
get_filename(&file_path).unwrap()
);
continue;
};
}
debug!("Deleted stale token {}", get_filename(&file_path).unwrap())
} }
} }
} }
}
Ok(())
} }
pub fn delete_key(email: &str) -> Result<(), Error> { pub fn delete_key(email: &str) -> Result<(), Error> {

View file

@ -1,4 +1,5 @@
use lettre::{transport::smtp::authentication::Credentials, SmtpTransport}; use lettre::{transport::smtp::authentication::Credentials, SmtpTransport};
use log::{debug, error, warn};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use sequoia_net::wkd::Variant; use sequoia_net::wkd::Variant;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -42,18 +43,27 @@ pub enum SMTPEncryption {
} }
fn get_settings() -> Settings { fn get_settings() -> Settings {
debug!("Reading settings...");
let content = match fs::read_to_string("wkd.toml") { let content = match fs::read_to_string("wkd.toml") {
Ok(content) => content, Ok(content) => content,
Err(_) => panic!("Unable to access settings file!"), Err(_) => {
error!("Unable to access settings file!");
panic!("Unable to access settings file!")
}
}; };
let settings = match toml::from_str(&content) { let settings = match toml::from_str(&content) {
Ok(settings) => settings, Ok(settings) => settings,
Err(_) => panic!("Unable to parse settings from file!"), Err(_) => {
error!("Unable to parse settings from file!");
panic!("Unable to parse settings from file!")
}
}; };
debug!("Successfully read setting!");
settings settings
} }
fn get_mailer() -> SmtpTransport { fn get_mailer() -> SmtpTransport {
debug!("Setting up SMTP...");
let creds = Credentials::new( let creds = Credentials::new(
SETTINGS.mail_settings.smtp_username.to_owned(), SETTINGS.mail_settings.smtp_username.to_owned(),
SETTINGS.mail_settings.smtp_password.to_owned(), SETTINGS.mail_settings.smtp_password.to_owned(),
@ -66,11 +76,18 @@ fn get_mailer() -> SmtpTransport {
}; };
let mailer = match builder { let mailer = match builder {
Ok(builder) => builder, Ok(builder) => builder,
Err(_) => panic!("Unable to set up smtp"), Err(_) => {
error!("Unable to set up smtp");
panic!("Unable to set up smtp")
}
} }
.credentials(creds) .credentials(creds)
.port(SETTINGS.mail_settings.smtp_port) .port(SETTINGS.mail_settings.smtp_port)
.build(); .build();
if mailer.test_connection().is_err() {
warn!("Connection test to smtp host failed!");
}
debug!("SMTP setup successful!");
mailer mailer
} }

View file

@ -60,6 +60,10 @@ pub fn key_exists(email: &str) -> Result<bool, Error> {
Ok(true) Ok(true)
} }
pub fn get_filename(path: &Path) -> Option<&str> {
path.file_name()?.to_str()
}
pub fn custom_format( pub fn custom_format(
w: &mut dyn std::io::Write, w: &mut dyn std::io::Write,
now: &mut DeferredNow, now: &mut DeferredNow,