vw_small

Hardened fork of Vaultwarden (https://github.com/dani-garcia/vaultwarden) with fewer features.
git clone https://git.philomathiclife.com/repos/vw_small
Log | Files | Refs | README

error.rs (10809B)


      1 //
      2 // Error generator macro
      3 //
      4 use core::error::Error as StdError;
      5 use core::fmt::{self, Debug, Display, Formatter};
      6 
      7 #[expect(edition_2024_expr_fragment_specifier, reason = "false positive")]
      8 macro_rules! make_error {
      9     ( $( $name:ident ( $ty:ty ): $src_fn:expr, $usr_msg_fun:expr ),+ $(,)? ) => {
     10         const BAD_REQUEST: u16 = 400;
     11         enum ErrorKind { $($name( $ty )),+ }
     12         #[allow(clippy::error_impl_error)]
     13         pub struct Error { message: String, error: ErrorKind, error_code: u16 }
     14 
     15         $(impl From<$ty> for Error {
     16             fn from(err: $ty) -> Self { Error::from((stringify!($name), err)) }
     17         })+
     18         $(impl<S: Into<String>> From<(S, $ty)> for Error {
     19             fn from(val: (S, $ty)) -> Self {
     20                 Error { message: val.0.into(), error: ErrorKind::$name(val.1), error_code: BAD_REQUEST }
     21             }
     22         })+
     23         impl StdError for Error {
     24             fn source(&self) -> Option<&(dyn StdError + 'static)> {
     25                 match self.error {$( ErrorKind::$name(ref e) => $src_fn(e), )+}
     26             }
     27         }
     28         impl Display for Error {
     29             fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
     30                 match self.error {$(
     31                    ErrorKind::$name(ref e) => f.write_str(&$usr_msg_fun(e, &self.message)),
     32                 )+}
     33             }
     34         }
     35     };
     36 }
     37 use core::any::Any;
     38 #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))]
     39 use core::convert::Infallible;
     40 use diesel::r2d2::PoolError as R2d2Err;
     41 use diesel::result::Error as DieselErr;
     42 use diesel::ConnectionError as DieselConErr;
     43 use jsonwebtoken::errors::Error as JwtErr;
     44 use openssl::error::ErrorStack as SSLErr;
     45 #[cfg(all(feature = "priv_sep", target_os = "openbsd"))]
     46 use priv_sep::UnveilErr;
     47 use rocket::error::Error as RocketErr;
     48 use serde_json::{Error as SerdeErr, Value};
     49 use std::io::Error as IoErr;
     50 use std::time::SystemTimeError as TimeErr;
     51 use webauthn_rs::prelude::WebauthnError as WebauthnErr;
     52 
     53 #[derive(Serialize)]
     54 struct Empty;
     55 
     56 // Error struct
     57 // Contains a String error message, meant for the user and an enum variant, with an error of different types.
     58 //
     59 // After the variant itself, there are two expressions. The first one indicates whether the error contains a source error (that we pretty print).
     60 // The second one contains the function used to obtain the response sent to the client
     61 #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))]
     62 make_error! {
     63     // Just an empty error
     64     Empty(Empty):     _no_source, _serialize,
     65     // Used to represent err! calls
     66     Simple(String):  _no_source,  _api_error,
     67     // Used for special return values, like 2FA errors
     68     Json(Value):     _no_source,  _serialize,
     69     Db(DieselErr):   _has_source, _api_error,
     70     R2d2(R2d2Err):   _has_source, _api_error,
     71     Serde(SerdeErr): _has_source, _api_error,
     72     JWt(JwtErr):     _has_source, _api_error,
     73 
     74     Io(IoErr):       _has_source, _api_error,
     75     Time(TimeErr):   _has_source, _api_error,
     76 
     77     OpenSSL(SSLErr):   _has_source, _api_error,
     78     Rocket(RocketErr): _has_source, _api_error,
     79 
     80     DieselCon(DieselConErr): _has_source, _api_error,
     81     Webauthn(WebauthnErr):   _has_source, _api_error,
     82 }
     83 // Error struct
     84 // Contains a String error message, meant for the user and an enum variant, with an error of different types.
     85 //
     86 // After the variant itself, there are two expressions. The first one indicates whether the error contains a source error (that we pretty print).
     87 // The second one contains the function used to obtain the response sent to the client
     88 #[cfg(all(feature = "priv_sep", target_os = "openbsd"))]
     89 make_error! {
     90     // Just an empty error
     91     Empty(Empty):     _no_source, _serialize,
     92     // Used to represent err! calls
     93     Simple(String):  _no_source,  _api_error,
     94     // Used for special return values, like 2FA errors
     95     Json(Value):     _no_source,  _serialize,
     96     Db(DieselErr):   _has_source, _api_error,
     97     R2d2(R2d2Err):   _has_source, _api_error,
     98     Serde(SerdeErr): _has_source, _api_error,
     99     JWt(JwtErr):     _has_source, _api_error,
    100 
    101     Io(IoErr):       _has_source, _api_error,
    102     Time(TimeErr):   _has_source, _api_error,
    103 
    104     OpenSSL(SSLErr):   _has_source, _api_error,
    105     Rocket(RocketErr): _has_source, _api_error,
    106     Unveil(UnveilErr): _has_source, _api_error,
    107 
    108     DieselCon(DieselConErr): _has_source, _api_error,
    109     Webauthn(WebauthnErr):   _has_source, _api_error,
    110 }
    111 #[cfg(not(all(feature = "priv_sep", target_os = "openbsd")))]
    112 impl From<Infallible> for Error {
    113     #[inline]
    114     fn from(value: Infallible) -> Self {
    115         match value {}
    116     }
    117 }
    118 impl Debug for Error {
    119     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
    120         match self.source() {
    121             Some(e) => write!(f, "{}.\n[CAUSE] {:#?}", self.message, e),
    122             None => match self.error {
    123                 ErrorKind::Empty(_) => Ok(()),
    124                 ErrorKind::Simple(ref s) => {
    125                     if &self.message == s {
    126                         write!(f, "{}", self.message)
    127                     } else {
    128                         write!(f, "{}. {}", self.message, s)
    129                     }
    130                 }
    131                 #[cfg(all(feature = "priv_sep", target_os = "openbsd"))]
    132                 ErrorKind::Unveil(ref err) => Display::fmt(err, f),
    133                 ErrorKind::Json(_) => write!(f, "{}", self.message),
    134                 ErrorKind::Db(_)
    135                 | ErrorKind::R2d2(_)
    136                 | ErrorKind::Serde(_)
    137                 | ErrorKind::JWt(_)
    138                 | ErrorKind::Io(_)
    139                 | ErrorKind::Time(_)
    140                 | ErrorKind::OpenSSL(_)
    141                 | ErrorKind::Rocket(_)
    142                 | ErrorKind::DieselCon(_)
    143                 | ErrorKind::Webauthn(_) => unreachable!(),
    144             },
    145         }
    146     }
    147 }
    148 
    149 impl Error {
    150     pub fn new<M: Into<String>, N: Into<String>>(usr_msg: M, log_msg: N) -> Self {
    151         (usr_msg, log_msg.into()).into()
    152     }
    153     #[must_use]
    154     pub fn empty() -> Self {
    155         Empty {}.into()
    156     }
    157     #[must_use]
    158     fn with_msg<M: Into<String>>(mut self, msg: M) -> Self {
    159         self.message = msg.into();
    160         self
    161     }
    162     #[must_use]
    163     pub const fn with_code(mut self, code: u16) -> Self {
    164         self.error_code = code;
    165         self
    166     }
    167 }
    168 
    169 pub trait MapResult<S> {
    170     fn map_res(self, msg: &str) -> Result<S, Error>;
    171 }
    172 
    173 impl<S, E: Into<Error>> MapResult<S> for Result<S, E> {
    174     fn map_res(self, msg: &str) -> Result<S, Error> {
    175         self.map_err(|e| e.into().with_msg(msg))
    176     }
    177 }
    178 
    179 impl<E: Into<Error>> MapResult<()> for Result<usize, E> {
    180     fn map_res(self, msg: &str) -> Result<(), Error> {
    181         self.and(Ok(())).map_res(msg)
    182     }
    183 }
    184 
    185 impl<S> MapResult<S> for Option<S> {
    186     fn map_res(self, msg: &str) -> Result<S, Error> {
    187         self.ok_or_else(|| Error::new(msg, ""))
    188     }
    189 }
    190 #[allow(clippy::unnecessary_wraps)]
    191 const fn _has_source<T>(e: T) -> Option<T> {
    192     Some(e)
    193 }
    194 fn _no_source<T, S>(_: T) -> Option<S> {
    195     None
    196 }
    197 
    198 fn _serialize(e: &impl serde::Serialize, _msg: &str) -> String {
    199     serde_json::to_string(e).unwrap()
    200 }
    201 
    202 fn _api_error(_: &impl Any, msg: &str) -> String {
    203     let json = json!({
    204         "Message": msg,
    205         "error": "",
    206         "error_description": "",
    207         "ValidationErrors": {"": [ msg ]},
    208         "ErrorModel": {
    209             "Message": msg,
    210             "Object": "error"
    211         },
    212         "ExceptionMessage": null,
    213         "ExceptionStackTrace": null,
    214         "InnerExceptionMessage": null,
    215         "Object": "error"
    216     });
    217     _serialize(&json, "")
    218 }
    219 
    220 //
    221 // Rocket responder impl
    222 //
    223 use rocket::http::{ContentType, Status};
    224 use rocket::request::Request;
    225 use rocket::response::{self, Responder, Response};
    226 use std::io::Cursor;
    227 
    228 impl<'r> Responder<'r, 'static> for Error {
    229     fn respond_to(self, _: &Request<'_>) -> response::Result<'static> {
    230         let code = Status::from_code(self.error_code).unwrap_or(Status::BadRequest);
    231         let body = self.to_string();
    232         Response::build()
    233             .status(code)
    234             .header(ContentType::JSON)
    235             .sized_body(Some(body.len()), Cursor::new(body))
    236             .ok()
    237     }
    238 }
    239 
    240 //
    241 // Error return macros
    242 //
    243 #[expect(edition_2024_expr_fragment_specifier, reason = "false positive")]
    244 #[macro_export]
    245 macro_rules! err {
    246     ($msg:expr) => {{
    247         return Err($crate::error::Error::new($msg, $msg));
    248     }};
    249     ($msg:expr, ErrorEvent $err_event:tt) => {{
    250         return Err($crate::error::Error::new($msg, $msg).with_event($crate::error::ErrorEvent $err_event));
    251     }};
    252     ($usr_msg:expr, $log_value:expr) => {{
    253         return Err($crate::error::Error::new($usr_msg, $log_value));
    254     }};
    255     ($usr_msg:expr, $log_value:expr, ErrorEvent $err_event:tt) => {{
    256         return Err($crate::error::Error::new($usr_msg, $log_value).with_event($crate::error::ErrorEvent $err_event));
    257     }};
    258 }
    259 
    260 #[expect(edition_2024_expr_fragment_specifier, reason = "false positive")]
    261 #[macro_export]
    262 macro_rules! err_silent {
    263     ($msg:expr) => {{
    264         return Err($crate::error::Error::new($msg, $msg));
    265     }};
    266     ($usr_msg:expr, $log_value:expr) => {{
    267         return Err($crate::error::Error::new($usr_msg, $log_value));
    268     }};
    269 }
    270 
    271 #[expect(edition_2024_expr_fragment_specifier, reason = "false positive")]
    272 #[macro_export]
    273 macro_rules! err_code {
    274     ($msg:expr, $err_code:expr) => {{
    275         return Err($crate::error::Error::new($msg, $msg).with_code($err_code));
    276     }};
    277     ($usr_msg:expr, $log_value:expr, $err_code:expr) => {{
    278         return Err($crate::error::Error::new($usr_msg, $log_value).with_code($err_code));
    279     }};
    280 }
    281 
    282 #[expect(edition_2024_expr_fragment_specifier, reason = "false positive")]
    283 #[macro_export]
    284 macro_rules! err_discard {
    285     ($msg:expr, $data:expr) => {{
    286         std::io::copy(&mut $data.open(), &mut std::io::sink()).ok();
    287         return Err($crate::error::Error::new($msg, $msg));
    288     }};
    289     ($usr_msg:expr, $log_value:expr, $data:expr) => {{
    290         std::io::copy(&mut $data.open(), &mut std::io::sink()).ok();
    291         return Err($crate::error::Error::new($usr_msg, $log_value));
    292     }};
    293 }
    294 
    295 #[expect(edition_2024_expr_fragment_specifier, reason = "false positive")]
    296 #[macro_export]
    297 macro_rules! err_json {
    298     ($expr:expr, $log_value:expr) => {{
    299         return Err(($log_value, $expr).into());
    300     }};
    301     ($expr:expr, $log_value:expr, $err_event:expr, ErrorEvent) => {{
    302         return Err(($log_value, $expr).into().with_event($err_event));
    303     }};
    304 }
    305 
    306 #[expect(edition_2024_expr_fragment_specifier, reason = "false positive")]
    307 #[macro_export]
    308 macro_rules! err_handler {
    309     ($expr:expr) => {{
    310         return ::rocket::request::Outcome::Error((rocket::http::Status::Unauthorized, $expr));
    311     }};
    312     ($usr_msg:expr, $log_value:expr) => {{
    313         return ::rocket::request::Outcome::Error((rocket::http::Status::Unauthorized, $usr_msg));
    314     }};
    315 }