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",
"serde",
"serde_json",
"thiserror",
"tokio",
]

View file

@ -13,4 +13,5 @@ sequoia-net = "0.27.0"
sequoia-openpgp = "1.14.0"
serde = { version = "1.0.160", features = ["derive"] }
serde_json = "1.0.96"
thiserror = "1.0.40"
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::utils::{get_email_from_cert, parse_pem};
use crate::PENDING;
@ -6,26 +7,42 @@ use crate::{pending_path, PATH, VARIANT};
use std::fs;
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 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() {
Action::Add => {
let cert = parse_pem(key.data());
let domain = get_email_from_cert(&cert)
.split('@')
.last()
.unwrap()
.to_owned();
sequoia_net::wkd::insert(PATH, domain, VARIANT, &cert).unwrap();
let cert = parse_pem(key.data())?;
let domain = match get_email_from_cert(&cert)?.split('@').last() {
Some(domain) => domain.to_string(),
None => return Err(Error::MalformedMail),
};
match sequoia_net::wkd::insert(PATH, domain, VARIANT, &cert) {
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) {
println!("Email sent to {}", email);
println!("Action: {:?}", action);
println!("Token: {}", token);
pub fn send_confirmation_email(email: &str, action: &Action, token: &str) {
println!("Email sent to {email}");
println!("Action: {action:?}");
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 errors;
mod management;
mod utils;
@ -40,7 +41,7 @@ async fn main() -> std::io::Result<()> {
let mut metronome = time::interval(time::Duration::from_secs(60 * 60 * 3));
loop {
metronome.tick().await;
clean_stale(MAX_AGE);
clean_stale(MAX_AGE).unwrap();
}
});
HttpServer::new(|| App::new().service(submit).service(confirm).service(delete))
@ -51,24 +52,24 @@ async fn main() -> std::io::Result<()> {
#[post("/api/submit")]
async fn submit(pem: web::Form<Pem>) -> Result<String> {
let cert = parse_pem(&pem.key);
let email = get_email_from_cert(&cert);
let cert = parse_pem(&pem.key)?;
let email = get_email_from_cert(&cert)?;
let token = gen_random_token();
store_pending_addition(pem.key.to_owned(), &token);
send_confirmation_email(&email, Action::Add, &token);
store_pending_addition(pem.key.clone(), &token)?;
send_confirmation_email(&email, &Action::Add, &token);
Ok(String::from("OK!"))
}
#[get("/api/confirm/{data}")]
async fn confirm(token: web::Path<Token>) -> Result<String> {
confirm_action(&token.data);
confirm_action(&token.data)?;
Ok(String::from("OK!"))
}
#[get("/api/delete/{address}")]
async fn delete(email: web::Path<Email>) -> Result<String> {
let token = gen_random_token();
store_pending_deletion(email.address.to_owned(), &token);
send_confirmation_email(&email.address, Action::Delete, &token);
store_pending_deletion(email.address.clone(), &token)?;
send_confirmation_email(&email.address, &Action::Delete, &token);
Ok(String::from("OK!"))
}

View file

@ -1,3 +1,4 @@
use crate::errors::Error;
use crate::utils::get_user_file_path;
use crate::PENDING;
use crate::{pending_path, PATH};
@ -19,65 +20,86 @@ pub struct Pending {
timestamp: i64,
}
impl Pending {
pub fn build_add(pem: String) -> Pending {
pub fn build_add(pem: String) -> Self {
let timestamp = Utc::now().timestamp();
Pending {
Self {
action: Action::Add,
data: pem,
timestamp,
}
}
pub fn build_delete(email: String) -> Pending {
pub fn build_delete(email: String) -> Self {
let timestamp = Utc::now().timestamp();
Pending {
Self {
action: Action::Delete,
data: email,
timestamp,
}
}
pub fn action(&self) -> &Action {
pub const fn action(&self) -> &Action {
&self.action
}
pub fn data(&self) -> &str {
&self.data
}
pub fn timestamp(&self) -> i64 {
pub const fn timestamp(&self) -> i64 {
self.timestamp
}
}
fn store_pending(pending: Pending, token: &str) {
let serialized = serde_json::to_string(&pending).unwrap();
fs::create_dir_all(pending_path!()).unwrap();
fs::write(pending_path!().join(token), serialized).unwrap();
}
pub fn store_pending_addition(pem: String, token: &str) {
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()
);
}
fn store_pending(pending: &Pending, token: &str) -> Result<(), Error> {
match serde_json::to_string(pending) {
Ok(serialized) => match fs::write(pending_path!().join(token), serialized) {
Ok(_) => Ok(()),
Err(_) => Err(Error::Inaccessible),
},
Err(_) => Err(Error::SerializeData),
}
}
pub fn delete_key(email: &str) {
let path = Path::new(PATH).join(get_user_file_path(email));
fs::remove_file(path).unwrap();
pub fn store_pending_addition(pem: String, token: &str) -> Result<(), Error> {
let data = Pending::build_add(pem);
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 rand::{distributions::Alphanumeric, thread_rng, Rng};
@ -12,8 +13,11 @@ macro_rules! pending_path {
};
}
pub fn parse_pem(data: &str) -> Cert {
sequoia_openpgp::Cert::from_bytes(data.as_bytes()).unwrap()
pub fn parse_pem(data: &str) -> Result<Cert, Error> {
match sequoia_openpgp::Cert::from_bytes(data.as_bytes()) {
Ok(data) => Ok(data),
Err(_) => Err(Error::ParseCert),
}
}
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()
}
pub fn get_email_from_cert(cert: &Cert) -> String {
cert.userids().next().unwrap().email().unwrap().unwrap()
pub fn get_email_from_cert(cert: &Cert) -> Result<String, Error> {
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 {
Url::from(email).unwrap().to_file_path(VARIANT).unwrap()
pub fn get_user_file_path(email: &str) -> Result<PathBuf, Error> {
match Url::from(email) {
Ok(data) => match data.to_file_path(VARIANT) {
Ok(data) => Ok(data),
Err(_) => Err(Error::ParseMail),
},
Err(_) => Err(Error::ParseMail),
}
}