mirror of
https://git.verdigado.com/NB-Public/simple-wkd.git
synced 2024-12-05 03:32:49 +01:00
Refactor some code
This commit is contained in:
parent
9c9f4793be
commit
52850bde10
7 changed files with 162 additions and 66 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1961,6 +1961,7 @@ dependencies = [
|
|||
"sequoia-openpgp",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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
35
src/errors.rs
Normal 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(),
|
||||
}
|
||||
}
|
||||
}
|
17
src/main.rs
17
src/main.rs
|
@ -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!"))
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
31
src/utils.rs
31
src/utils.rs
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue