mirror of
https://git.verdigado.com/NB-Public/simple-wkd.git
synced 2024-10-30 05:05:52 +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",
|
"sequoia-openpgp",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -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"] }
|
||||||
|
|
|
@ -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
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 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!"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
31
src/utils.rs
31
src/utils.rs
|
@ -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),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue