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:
parent
b717852376
commit
8484febb9d
5 changed files with 154 additions and 43 deletions
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
19
src/main.rs
19
src/main.rs
|
@ -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!"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue