1
0
Fork 1
Spiegel von https://github.com/dani-garcia/vaultwarden.git synchronisiert 2024-05-20 16:00:04 +02:00
vaultwarden/src/error.rs
BlackDex 5b430f22bc
Support all DB's for Alpine and Debian
- Using my own rust-musl build containers we now support all database
types for both Debian and Alpine.
- Added new Alpine containers for armv6 and arm64/aarch64
- The Debian builds can also be done wihout dpkg magic stuff, probably
some fixes in Rust regarding linking (Or maybe OpenSSL or Diesel), in
any case, it works now without hacking dpkg and apt.
- Updated toolchain and crates
2021-12-26 21:59:28 +01:00

276 Zeilen
8,5 KiB
Rust

//
// Error generator macro
//
use std::error::Error as StdError;
macro_rules! make_error {
( $( $name:ident ( $ty:ty ): $src_fn:expr, $usr_msg_fun:expr ),+ $(,)? ) => {
const BAD_REQUEST: u16 = 400;
pub enum ErrorKind { $($name( $ty )),+ }
pub struct Error { message: String, error: ErrorKind, error_code: u16 }
$(impl From<$ty> for Error {
fn from(err: $ty) -> Self { Error::from((stringify!($name), err)) }
})+
$(impl<S: Into<String>> From<(S, $ty)> for Error {
fn from(val: (S, $ty)) -> Self {
Error { message: val.0.into(), error: ErrorKind::$name(val.1), error_code: BAD_REQUEST }
}
})+
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
match &self.error {$( ErrorKind::$name(e) => $src_fn(e), )+}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match &self.error {$(
ErrorKind::$name(e) => f.write_str(&$usr_msg_fun(e, &self.message)),
)+}
}
}
};
}
use diesel::r2d2::PoolError as R2d2Err;
use diesel::result::Error as DieselErr;
use diesel::ConnectionError as DieselConErr;
use diesel_migrations::RunMigrationsError as DieselMigErr;
use handlebars::RenderError as HbErr;
use jsonwebtoken::errors::Error as JwtErr;
use lettre::address::AddressError as AddrErr;
use lettre::error::Error as LettreErr;
use lettre::transport::smtp::Error as SmtpErr;
use openssl::error::ErrorStack as SSLErr;
use regex::Error as RegexErr;
use reqwest::Error as ReqErr;
use serde_json::{Error as SerdeErr, Value};
use std::io::Error as IoErr;
use std::time::SystemTimeError as TimeErr;
use u2f::u2ferror::U2fError as U2fErr;
use webauthn_rs::error::WebauthnError as WebauthnErr;
use yubico::yubicoerror::YubicoError as YubiErr;
#[derive(Serialize)]
pub struct Empty {}
// Error struct
// Contains a String error message, meant for the user and an enum variant, with an error of different types.
//
// After the variant itself, there are two expressions. The first one indicates whether the error contains a source error (that we pretty print).
// The second one contains the function used to obtain the response sent to the client
make_error! {
// Just an empty error
Empty(Empty): _no_source, _serialize,
// Used to represent err! calls
Simple(String): _no_source, _api_error,
// Used for special return values, like 2FA errors
Json(Value): _no_source, _serialize,
Db(DieselErr): _has_source, _api_error,
R2d2(R2d2Err): _has_source, _api_error,
U2f(U2fErr): _has_source, _api_error,
Serde(SerdeErr): _has_source, _api_error,
JWt(JwtErr): _has_source, _api_error,
Handlebars(HbErr): _has_source, _api_error,
Io(IoErr): _has_source, _api_error,
Time(TimeErr): _has_source, _api_error,
Req(ReqErr): _has_source, _api_error,
Regex(RegexErr): _has_source, _api_error,
Yubico(YubiErr): _has_source, _api_error,
Lettre(LettreErr): _has_source, _api_error,
Address(AddrErr): _has_source, _api_error,
Smtp(SmtpErr): _has_source, _api_error,
OpenSSL(SSLErr): _has_source, _api_error,
DieselCon(DieselConErr): _has_source, _api_error,
DieselMig(DieselMigErr): _has_source, _api_error,
Webauthn(WebauthnErr): _has_source, _api_error,
}
impl std::fmt::Debug for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self.source() {
Some(e) => write!(f, "{}.\n[CAUSE] {:#?}", self.message, e),
None => match self.error {
ErrorKind::Empty(_) => Ok(()),
ErrorKind::Simple(ref s) => {
if &self.message == s {
write!(f, "{}", self.message)
} else {
write!(f, "{}. {}", self.message, s)
}
}
ErrorKind::Json(_) => write!(f, "{}", self.message),
_ => unreachable!(),
},
}
}
}
impl Error {
pub fn new<M: Into<String>, N: Into<String>>(usr_msg: M, log_msg: N) -> Self {
(usr_msg, log_msg.into()).into()
}
pub fn empty() -> Self {
Empty {}.into()
}
#[must_use]
pub fn with_msg<M: Into<String>>(mut self, msg: M) -> Self {
self.message = msg.into();
self
}
#[must_use]
pub const fn with_code(mut self, code: u16) -> Self {
self.error_code = code;
self
}
}
pub trait MapResult<S> {
fn map_res(self, msg: &str) -> Result<S, Error>;
}
impl<S, E: Into<Error>> MapResult<S> for Result<S, E> {
fn map_res(self, msg: &str) -> Result<S, Error> {
self.map_err(|e| e.into().with_msg(msg))
}
}
impl<E: Into<Error>> MapResult<()> for Result<usize, E> {
fn map_res(self, msg: &str) -> Result<(), Error> {
self.and(Ok(())).map_res(msg)
}
}
impl<S> MapResult<S> for Option<S> {
fn map_res(self, msg: &str) -> Result<S, Error> {
self.ok_or_else(|| Error::new(msg, ""))
}
}
#[allow(clippy::unnecessary_wraps)]
const fn _has_source<T>(e: T) -> Option<T> {
Some(e)
}
fn _no_source<T, S>(_: T) -> Option<S> {
None
}
fn _serialize(e: &impl serde::Serialize, _msg: &str) -> String {
serde_json::to_string(e).unwrap()
}
fn _api_error(_: &impl std::any::Any, msg: &str) -> String {
let json = json!({
"Message": msg,
"error": "",
"error_description": "",
"ValidationErrors": {"": [ msg ]},
"ErrorModel": {
"Message": msg,
"Object": "error"
},
"ExceptionMessage": null,
"ExceptionStackTrace": null,
"InnerExceptionMessage": null,
"Object": "error"
});
_serialize(&json, "")
}
//
// Rocket responder impl
//
use std::io::Cursor;
use rocket::http::{ContentType, Status};
use rocket::request::Request;
use rocket::response::{self, Responder, Response};
impl<'r> Responder<'r> for Error {
fn respond_to(self, _: &Request) -> response::Result<'r> {
match self.error {
ErrorKind::Empty(_) => {} // Don't print the error in this situation
ErrorKind::Simple(_) => {} // Don't print the error in this situation
_ => error!(target: "error", "{:#?}", self),
};
let code = Status::from_code(self.error_code).unwrap_or(Status::BadRequest);
Response::build().status(code).header(ContentType::JSON).sized_body(Cursor::new(format!("{}", self))).ok()
}
}
//
// Error return macros
//
#[macro_export]
macro_rules! err {
($msg:expr) => {{
error!("{}", $msg);
return Err(crate::error::Error::new($msg, $msg));
}};
($usr_msg:expr, $log_value:expr) => {{
error!("{}. {}", $usr_msg, $log_value);
return Err(crate::error::Error::new($usr_msg, $log_value));
}};
}
macro_rules! err_silent {
($msg:expr) => {{
return Err(crate::error::Error::new($msg, $msg));
}};
($usr_msg:expr, $log_value:expr) => {{
return Err(crate::error::Error::new($usr_msg, $log_value));
}};
}
#[macro_export]
macro_rules! err_code {
($msg:expr, $err_code: expr) => {{
error!("{}", $msg);
return Err(crate::error::Error::new($msg, $msg).with_code($err_code));
}};
($usr_msg:expr, $log_value:expr, $err_code: expr) => {{
error!("{}. {}", $usr_msg, $log_value);
return Err(crate::error::Error::new($usr_msg, $log_value).with_code($err_code));
}};
}
#[macro_export]
macro_rules! err_discard {
($msg:expr, $data:expr) => {{
std::io::copy(&mut $data.open(), &mut std::io::sink()).ok();
return Err(crate::error::Error::new($msg, $msg));
}};
($usr_msg:expr, $log_value:expr, $data:expr) => {{
std::io::copy(&mut $data.open(), &mut std::io::sink()).ok();
return Err(crate::error::Error::new($usr_msg, $log_value));
}};
}
#[macro_export]
macro_rules! err_json {
($expr:expr, $log_value:expr) => {{
return Err(($log_value, $expr).into());
}};
}
#[macro_export]
macro_rules! err_handler {
($expr:expr) => {{
error!(target: "auth", "Unauthorized Error: {}", $expr);
return ::rocket::request::Outcome::Failure((rocket::http::Status::Unauthorized, $expr));
}};
($usr_msg:expr, $log_value:expr) => {{
error!(target: "auth", "Unauthorized Error: {}. {}", $usr_msg, $log_value);
return ::rocket::request::Outcome::Failure((rocket::http::Status::Unauthorized, $usr_msg));
}};
}