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 (10669B)


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