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

Refactor some code

This commit is contained in:
Delta1925 2023-04-13 22:00:33 +02:00
parent 9c9f4793be
commit 52850bde10
No known key found for this signature in database
GPG key ID: 1C21ACE44193CB25
7 changed files with 162 additions and 66 deletions

1
Cargo.lock generated
View file

@ -1961,6 +1961,7 @@ dependencies = [
"sequoia-openpgp", "sequoia-openpgp",
"serde", "serde",
"serde_json", "serde_json",
"thiserror",
"tokio", "tokio",
] ]

View file

@ -13,4 +13,5 @@ sequoia-net = "0.27.0"
sequoia-openpgp = "1.14.0" sequoia-openpgp = "1.14.0"
serde = { version = "1.0.160", features = ["derive"] } serde = { version = "1.0.160", features = ["derive"] }
serde_json = "1.0.96" serde_json = "1.0.96"
thiserror = "1.0.40"
tokio = { version = "1.27.0", features = ["time"] } tokio = { version = "1.27.0", features = ["time"] }

View file

@ -1,3 +1,4 @@
use crate::errors::Error;
use crate::management::{delete_key, Action, Pending}; use crate::management::{delete_key, Action, Pending};
use crate::utils::{get_email_from_cert, parse_pem}; use crate::utils::{get_email_from_cert, parse_pem};
use crate::PENDING; use crate::PENDING;
@ -6,26 +7,42 @@ use crate::{pending_path, PATH, VARIANT};
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
pub fn confirm_action(token: &str) { pub fn confirm_action(token: &str) -> Result<(), Error> {
let pending_path = pending_path!().join(token); let pending_path = pending_path!().join(token);
let key: Pending = serde_json::from_str(&fs::read_to_string(&pending_path).unwrap()).unwrap(); let data = if pending_path.exists() {
match fs::read_to_string(&pending_path) {
Ok(data) => data,
Err(_) => return Err(Error::Inaccessible),
}
} else {
return Err(Error::MissingPath);
};
let key = match serde_json::from_str::<Pending>(&data) {
Ok(key) => key,
Err(_) => return Err(Error::ParseStored),
};
match key.action() { match key.action() {
Action::Add => { Action::Add => {
let cert = parse_pem(key.data()); let cert = parse_pem(key.data())?;
let domain = get_email_from_cert(&cert) let domain = match get_email_from_cert(&cert)?.split('@').last() {
.split('@') Some(domain) => domain.to_string(),
.last() None => return Err(Error::MalformedMail),
.unwrap() };
.to_owned(); match sequoia_net::wkd::insert(PATH, domain, VARIANT, &cert) {
sequoia_net::wkd::insert(PATH, domain, VARIANT, &cert).unwrap(); Ok(_) => (),
Err(_) => return Err(Error::AddingKey),
}
} }
Action::Delete => delete_key(key.data()), Action::Delete => delete_key(key.data())?,
}
match fs::remove_file(&pending_path) {
Ok(_) => Ok(()),
Err(_) => Err(Error::Inaccessible),
} }
fs::remove_file(&pending_path).unwrap();
} }
pub fn send_confirmation_email(email: &str, action: Action, token: &str) { pub fn send_confirmation_email(email: &str, action: &Action, token: &str) {
println!("Email sent to {}", email); println!("Email sent to {email}");
println!("Action: {:?}", action); println!("Action: {action:?}");
println!("Token: {}", token); println!("Token: {token}");
} }

35
src/errors.rs Normal file
View file

@ -0,0 +1,35 @@
use actix_web::http::StatusCode;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("Error while parsing cert")]
ParseCert,
#[error("Error while parsing E-Mail")]
ParseMail,
#[error("Error while parsing stored data")]
ParseStored,
#[error("No E-Mail found in the certificate")]
MissingMail,
#[error("The E-Mail is malformed")]
MalformedMail,
#[error("Error while serializing data")]
SerializeData,
#[error("Error while deserializing data")]
DeserializeData,
#[error("File or directory does not exist")]
MissingPath,
#[error("The file is inaccessible")]
Inaccessible,
#[error("Error while adding a key to the wkd")]
AddingKey,
}
impl actix_web::ResponseError for Error {
fn status_code(&self) -> actix_web::http::StatusCode {
match self {
Self::MissingPath => StatusCode::from_u16(404).unwrap(),
_ => StatusCode::from_u16(500).unwrap(),
}
}
}

View file

@ -1,4 +1,5 @@
mod confirmation; mod confirmation;
mod errors;
mod management; mod management;
mod utils; mod utils;
@ -40,7 +41,7 @@ async fn main() -> std::io::Result<()> {
let mut metronome = time::interval(time::Duration::from_secs(60 * 60 * 3)); let mut metronome = time::interval(time::Duration::from_secs(60 * 60 * 3));
loop { loop {
metronome.tick().await; metronome.tick().await;
clean_stale(MAX_AGE); clean_stale(MAX_AGE).unwrap();
} }
}); });
HttpServer::new(|| App::new().service(submit).service(confirm).service(delete)) HttpServer::new(|| App::new().service(submit).service(confirm).service(delete))
@ -51,24 +52,24 @@ async fn main() -> std::io::Result<()> {
#[post("/api/submit")] #[post("/api/submit")]
async fn submit(pem: web::Form<Pem>) -> Result<String> { 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.to_owned(), &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("OK!")) Ok(String::from("OK!"))
} }
#[get("/api/confirm/{data}")] #[get("/api/confirm/{data}")]
async fn confirm(token: web::Path<Token>) -> Result<String> { async fn confirm(token: web::Path<Token>) -> Result<String> {
confirm_action(&token.data); confirm_action(&token.data)?;
Ok(String::from("OK!")) Ok(String::from("OK!"))
} }
#[get("/api/delete/{address}")] #[get("/api/delete/{address}")]
async fn delete(email: web::Path<Email>) -> Result<String> { async fn delete(email: web::Path<Email>) -> Result<String> {
let token = gen_random_token(); let token = gen_random_token();
store_pending_deletion(email.address.to_owned(), &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("OK!")) Ok(String::from("OK!"))
} }

View file

@ -1,3 +1,4 @@
use crate::errors::Error;
use crate::utils::get_user_file_path; use crate::utils::get_user_file_path;
use crate::PENDING; use crate::PENDING;
use crate::{pending_path, PATH}; use crate::{pending_path, PATH};
@ -19,65 +20,86 @@ pub struct Pending {
timestamp: i64, timestamp: i64,
} }
impl Pending { impl Pending {
pub fn build_add(pem: String) -> Pending { pub fn build_add(pem: String) -> Self {
let timestamp = Utc::now().timestamp(); let timestamp = Utc::now().timestamp();
Pending { Self {
action: Action::Add, action: Action::Add,
data: pem, data: pem,
timestamp, timestamp,
} }
} }
pub fn build_delete(email: String) -> Pending { pub fn build_delete(email: String) -> Self {
let timestamp = Utc::now().timestamp(); let timestamp = Utc::now().timestamp();
Pending { Self {
action: Action::Delete, action: Action::Delete,
data: email, data: email,
timestamp, timestamp,
} }
} }
pub fn action(&self) -> &Action { pub const fn action(&self) -> &Action {
&self.action &self.action
} }
pub fn data(&self) -> &str { pub fn data(&self) -> &str {
&self.data &self.data
} }
pub fn timestamp(&self) -> i64 { pub const fn timestamp(&self) -> i64 {
self.timestamp self.timestamp
} }
} }
fn store_pending(pending: Pending, token: &str) { fn store_pending(pending: &Pending, token: &str) -> Result<(), Error> {
let serialized = serde_json::to_string(&pending).unwrap(); match serde_json::to_string(pending) {
fs::create_dir_all(pending_path!()).unwrap(); Ok(serialized) => match fs::write(pending_path!().join(token), serialized) {
fs::write(pending_path!().join(token), serialized).unwrap(); Ok(_) => Ok(()),
} Err(_) => Err(Error::Inaccessible),
},
pub fn store_pending_addition(pem: String, token: &str) { Err(_) => Err(Error::SerializeData),
let data = Pending::build_add(pem);
store_pending(data, token);
}
pub fn store_pending_deletion(email: String, token: &str) {
let data = Pending::build_delete(email);
store_pending(data, token);
}
pub fn clean_stale(max_age: i64) {
for path in fs::read_dir(pending_path!()).unwrap() {
let file_path = path.unwrap().path();
let key: Pending = serde_json::from_str(&fs::read_to_string(&file_path).unwrap()).unwrap();
let now = Utc::now().timestamp();
if now - key.timestamp() > max_age {
fs::remove_file(&file_path).unwrap();
println!(
"Deleted {}, since it was stale",
&file_path.to_str().unwrap()
);
}
} }
} }
pub fn delete_key(email: &str) { pub fn store_pending_addition(pem: String, token: &str) -> Result<(), Error> {
let path = Path::new(PATH).join(get_user_file_path(email)); let data = Pending::build_add(pem);
fs::remove_file(path).unwrap(); store_pending(&data, token)?;
Ok(())
}
pub fn store_pending_deletion(email: String, token: &str) -> Result<(), Error> {
let data = Pending::build_delete(email);
store_pending(&data, token)?;
Ok(())
}
pub fn clean_stale(max_age: i64) -> Result<(), Error> {
for path in fs::read_dir(pending_path!()).unwrap().flatten() {
let file_path = path.path();
if file_path.exists() {
match fs::read_to_string(&file_path) {
Ok(data) => match serde_json::from_str::<Pending>(&data) {
Ok(key) => {
let now = Utc::now().timestamp();
if now - key.timestamp() > max_age {
if fs::remove_file(&file_path).is_err() {
return Err(Error::Inaccessible);
}
println!(
"Deleted {}, since it was stale",
&file_path.to_str().unwrap()
);
}
}
Err(_) => return Err(Error::DeserializeData),
},
Err(_) => return Err(Error::Inaccessible),
}
}
}
Ok(())
}
pub fn delete_key(email: &str) -> Result<(), Error> {
let path = Path::new(PATH).join(get_user_file_path(email)?);
match fs::remove_file(path) {
Ok(_) => Ok(()),
Err(_) => Err(Error::Inaccessible),
}
} }

View file

@ -1,3 +1,4 @@
use crate::errors::Error;
use crate::VARIANT; use crate::VARIANT;
use rand::{distributions::Alphanumeric, thread_rng, Rng}; use rand::{distributions::Alphanumeric, thread_rng, Rng};
@ -12,8 +13,11 @@ macro_rules! pending_path {
}; };
} }
pub fn parse_pem(data: &str) -> Cert { pub fn parse_pem(data: &str) -> Result<Cert, Error> {
sequoia_openpgp::Cert::from_bytes(data.as_bytes()).unwrap() match sequoia_openpgp::Cert::from_bytes(data.as_bytes()) {
Ok(data) => Ok(data),
Err(_) => Err(Error::ParseCert),
}
} }
pub fn gen_random_token() -> String { pub fn gen_random_token() -> String {
@ -21,10 +25,25 @@ pub fn gen_random_token() -> String {
(0..10).map(|_| rng.sample(Alphanumeric) as char).collect() (0..10).map(|_| rng.sample(Alphanumeric) as char).collect()
} }
pub fn get_email_from_cert(cert: &Cert) -> String { pub fn get_email_from_cert(cert: &Cert) -> Result<String, Error> {
cert.userids().next().unwrap().email().unwrap().unwrap() match cert.userids().next() {
Some(data) => match data.email() {
Ok(data) => match data {
Some(data) => Ok(data),
None => Err(Error::MissingMail),
},
Err(_) => Err(Error::ParseMail),
},
None => Err(Error::ParseCert),
}
} }
pub fn get_user_file_path(email: &str) -> PathBuf { pub fn get_user_file_path(email: &str) -> Result<PathBuf, Error> {
Url::from(email).unwrap().to_file_path(VARIANT).unwrap() match Url::from(email) {
Ok(data) => match data.to_file_path(VARIANT) {
Ok(data) => Ok(data),
Err(_) => Err(Error::ParseMail),
},
Err(_) => Err(Error::ParseMail),
}
} }