From 85b2c707b8ef2ad93ace0a950d0236c10596e24b Mon Sep 17 00:00:00 2001 From: Delta1925 Date: Mon, 17 Apr 2023 23:22:58 +0200 Subject: [PATCH] Improve error handling further --- backend/src/confirmation.rs | 11 +++++----- backend/src/errors.rs | 30 +++++++++++++++---------- backend/src/main.rs | 15 +++++-------- backend/src/settings.rs | 4 +++- backend/src/utils.rs | 44 ++++++++++++++++++++++++++----------- 5 files changed, 64 insertions(+), 40 deletions(-) diff --git a/backend/src/confirmation.rs b/backend/src/confirmation.rs index 5a79192..20ae5fd 100644 --- a/backend/src/confirmation.rs +++ b/backend/src/confirmation.rs @@ -6,7 +6,7 @@ use crate::management::{delete_key, Action, Pending}; use crate::pending_path; use crate::settings::{MAILER, ROOT_FOLDER, SETTINGS}; use crate::utils::{get_email_from_cert, parse_pem, read_file}; -use anyhow::{anyhow, bail, Result}; +use anyhow::Result; use lettre::{Message, Transport}; use std::fs; @@ -26,9 +26,7 @@ pub fn confirm_action(token: &str) -> Result<(Action, String)> { let email = get_email_from_cert(&cert)?; let domain = match email.split('@').last() { Some(domain) => domain.to_string(), - None => { - Err(SpecialErrors::MalformedEmail)? - } + None => Err(SpecialErrors::MalformedEmail)?, }; sequoia_net::wkd::insert(ROOT_FOLDER, domain, SETTINGS.variant, &cert)?; email @@ -59,7 +57,10 @@ pub fn send_confirmation_email(address: &str, action: &Action, token: &str) -> R panic!("Unable to parse the email in the settings!") } }) - .to(address.parse()?) + .to(match address.parse() { + Ok(mbox) => mbox, + Err(_) => Err(SpecialErrors::MalformedEmail)?, + }) .subject( SETTINGS .mail_settings diff --git a/backend/src/errors.rs b/backend/src/errors.rs index 496e45e..dce8921 100644 --- a/backend/src/errors.rs +++ b/backend/src/errors.rs @@ -7,14 +7,18 @@ use crate::utils::return_outcome; #[derive(Debug, DeriveError)] pub enum SpecialErrors { + #[error("Could not find any primay user email in the keyblock!")] + EmailMissing, #[error("The request had expired!")] ExpiredRequest, #[error("The key for the requested user does not exist!")] InexistingUser, + #[error("The key is either expired or uses an obsolete cipher!")] + InvalidCert, + #[error("Could not parse keyblock")] + MalformedCert, #[error("Could not parse user email: malformed email")] MalformedEmail, - #[error("Could not find any primay user email in the keyblock!")] - MalformedCert, #[error("The requested file does not exist!")] MissingFile, #[error("User email rejected: domain not allowed")] @@ -55,20 +59,22 @@ impl From for CompatErr { impl ResponseError for CompatErr { fn status_code(&self) -> actix_web::http::StatusCode { match self { - Self::AnyhowErr(_) => StatusCode::INTERNAL_SERVER_ERROR, - Self::SpecialErr(error) => match error { - SpecialErrors::ExpiredRequest => StatusCode::BAD_REQUEST, - SpecialErrors::InexistingUser => StatusCode::NOT_FOUND, - SpecialErrors::MalformedCert => StatusCode::BAD_REQUEST, - SpecialErrors::MalformedEmail => StatusCode::BAD_REQUEST, - SpecialErrors::MissingFile => StatusCode::NOT_FOUND, - SpecialErrors::UnallowedDomain => StatusCode::UNAUTHORIZED, - } + Self::AnyhowErr(_) => StatusCode::INTERNAL_SERVER_ERROR, + Self::SpecialErr(error) => match error { + SpecialErrors::ExpiredRequest => StatusCode::BAD_REQUEST, + SpecialErrors::InexistingUser => StatusCode::NOT_FOUND, + SpecialErrors::InvalidCert => StatusCode::BAD_REQUEST, + SpecialErrors::EmailMissing => StatusCode::BAD_REQUEST, + SpecialErrors::MalformedCert => StatusCode::BAD_REQUEST, + SpecialErrors::MalformedEmail => StatusCode::BAD_REQUEST, + SpecialErrors::MissingFile => StatusCode::NOT_FOUND, + SpecialErrors::UnallowedDomain => StatusCode::UNAUTHORIZED, + }, } } fn error_response(&self) -> actix_web::HttpResponse { - match return_outcome(Err(&self.to_string())) { + match return_outcome(Err(self)) { Ok(httpbuilder) => httpbuilder, Err(_) => HttpResponseBuilder::new(self.status_code()).body(self.to_string()), } diff --git a/backend/src/main.rs b/backend/src/main.rs index 59a6c83..f96387d 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -9,7 +9,7 @@ use crate::errors::CompatErr; use crate::management::{clean_stale, store_pending_addition, store_pending_deletion, Action}; use crate::settings::{ROOT_FOLDER, SETTINGS}; use crate::utils::{ - gen_random_token, get_email_from_cert, is_email_allowed, parse_pem, return_outcome, read_file, + gen_random_token, get_email_from_cert, is_email_allowed, parse_pem, read_file, return_outcome, }; use actix_files::Files; @@ -18,7 +18,6 @@ use actix_web::http::StatusCode; use actix_web::{ get, post, web, App, HttpRequest, HttpResponse, HttpResponseBuilder, HttpServer, Result, }; -use anyhow::anyhow; use errors::SpecialErrors; use serde::Deserialize; use std::env; @@ -108,12 +107,8 @@ async fn submit(pem: web::Form) -> Result { async fn confirm(token: web::Query) -> Result { let (action, _email) = confirm_action(&token.token)?; match action { - Action::Add => { - Ok(return_outcome(Ok("Your key was added successfully!"))?) - } - Action::Delete => { - Ok(return_outcome(Ok("Your key was deleted successfully!"))?) - } + Action::Add => Ok(return_outcome(Ok("Your key was added successfully!"))?), + Action::Delete => Ok(return_outcome(Ok("Your key was deleted successfully!"))?), } } @@ -122,5 +117,7 @@ async fn delete(email: web::Query) -> Result { let token = gen_random_token(); store_pending_deletion(email.email.clone(), &token)?; send_confirmation_email(&email.email, &Action::Delete, &token)?; - Ok(return_outcome(Ok("You requested the deletion of your key successfully!"))?) + Ok(return_outcome(Ok( + "You requested the deletion of your key successfully!", + ))?) } diff --git a/backend/src/settings.rs b/backend/src/settings.rs index cb75eb5..13dfa86 100644 --- a/backend/src/settings.rs +++ b/backend/src/settings.rs @@ -1,8 +1,9 @@ use lettre::{transport::smtp::authentication::Credentials, SmtpTransport}; use once_cell::sync::Lazy; use sequoia_net::wkd::Variant; +use sequoia_openpgp::policy::StandardPolicy; use serde::{Deserialize, Serialize}; -use std::{path::{PathBuf}}; +use std::path::PathBuf; use url::Url; use crate::utils::read_file; @@ -84,6 +85,7 @@ fn get_mailer() -> SmtpTransport { mailer } +pub const POLICY: &StandardPolicy = &StandardPolicy::new(); pub const ROOT_FOLDER: &str = "data"; pub static SETTINGS: Lazy = Lazy::new(get_settings); pub static MAILER: Lazy = Lazy::new(get_mailer); diff --git a/backend/src/utils.rs b/backend/src/utils.rs index bf984c6..3d03cb5 100644 --- a/backend/src/utils.rs +++ b/backend/src/utils.rs @@ -1,18 +1,21 @@ +use crate::errors::CompatErr; use crate::errors::SpecialErrors; +use crate::settings::POLICY; use crate::settings::ROOT_FOLDER; use crate::settings::SETTINGS; +use actix_web::ResponseError; use actix_web::{ http::{header::ContentType, StatusCode}, HttpResponse, HttpResponseBuilder, }; -use anyhow::{anyhow, bail, Result}; +use anyhow::Result; use flexi_logger::{ detailed_format, style, DeferredNow, FileSpec, FlexiLoggerError, Logger, LoggerHandle, Record, }; use rand::{distributions::Alphanumeric, thread_rng, Rng}; use sequoia_net::wkd::Url; -use sequoia_openpgp::{parse::Parse, policy::StandardPolicy, Cert}; +use sequoia_openpgp::{parse::Parse, Cert}; use std::{ fs, path::{Path, PathBuf}, @@ -32,6 +35,16 @@ macro_rules! webpage_path { }; } +#[macro_export] +macro_rules! validate_cert { + ( $x:expr ) => { + match $x.with_policy(POLICY, None) { + Ok(validcert) => Ok(validcert), + Err(_) => Err(SpecialErrors::InvalidCert), + } + }; +} + pub fn read_file(path: &PathBuf) -> Result { if path.is_file() { Ok(fs::read_to_string(path)?) @@ -52,9 +65,11 @@ pub fn is_email_allowed(email: &str) -> Result<()> { } pub fn parse_pem(pemfile: &str) -> Result { - let cert = sequoia_openpgp::Cert::from_bytes(pemfile.as_bytes())?; - let policy = StandardPolicy::new(); - cert.with_policy(&policy, None)?; + let cert = match sequoia_openpgp::Cert::from_bytes(pemfile.as_bytes()) { + Ok(cert) => cert, + Err(_) => Err(SpecialErrors::MalformedCert)?, + }; + validate_cert!(cert)?; Ok(cert) } @@ -64,13 +79,12 @@ pub fn gen_random_token() -> String { } pub fn get_email_from_cert(cert: &Cert) -> Result { - let policy = StandardPolicy::new(); - let validcert = cert.with_policy(&policy, None)?; + let validcert = validate_cert!(cert)?; let userid_opt = validcert.primary_userid()?; let email_opt = userid_opt.email()?; match email_opt { Some(email) => Ok(email), - None => Err(SpecialErrors::MalformedCert)?, + None => Err(SpecialErrors::EmailMissing)?, } } @@ -135,15 +149,19 @@ pub fn init_logger() -> Result { .start() } -pub fn return_outcome(data: Result<&str, &str>) -> Result { +pub fn return_outcome(data: Result<&str, &CompatErr>) -> Result { let path = webpage_path!().join("status").join("index.html"); let template = read_file(&path)?; let (page, message) = match data { - Ok(message) => (template.replace("((%s))", "Success!"), message), - Err(message) => (template.replace("((%s))", "Failure!"), message), + Ok(message) => (template.replace("((%s))", "Success!"), message.to_string()), + Err(error) => (template.replace("((%s))", "Failure!"), error.to_string()), }; - let page = page.replace("((%m))", message); - return Ok(HttpResponseBuilder::new(StatusCode::OK) + let status_code = match data { + Ok(_) => StatusCode::OK, + Err(error) => error.status_code(), + }; + let page = page.replace("((%m))", &message); + return Ok(HttpResponseBuilder::new(status_code) .insert_header(ContentType::html()) .body(page)); }