151 lines
4.4 KiB
Rust
151 lines
4.4 KiB
Rust
//! Handle high-level errors that should be converted into HTTP
|
|
//! responses.
|
|
//!
|
|
//! The [Error] type defined in this module is used by route handlers
|
|
//! to convert errors into HTTP responses. Some error types (such as
|
|
//! [db::Error]) are automatcially converted into [Error]. [Error]
|
|
//! also supplies some constructors, such as [Error::new_unexpected()]
|
|
//! and [Error::new_unauthorized] that can be used directly.
|
|
//!
|
|
//! The intended use is that error types which have [From] defined
|
|
//! might sometimes be handled at a higher level. If an error should
|
|
//! always be handled here then other modules should call the
|
|
//! appropriate [Error] constructor directly.
|
|
|
|
use {
|
|
crate::{authentication, db},
|
|
askama::Template,
|
|
askama_axum::{IntoResponse, Response},
|
|
http::status::StatusCode,
|
|
tracing::{debug, error, info},
|
|
};
|
|
|
|
#[derive(Debug)]
|
|
enum ErrorType {
|
|
InternalServerError,
|
|
Unauthorized,
|
|
JwtExpired(db::User),
|
|
}
|
|
|
|
impl std::fmt::Display for ErrorType {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
ErrorType::InternalServerError => write!(f, "InternalServerError"),
|
|
ErrorType::Unauthorized => write!(f, "Unauthorized"),
|
|
ErrorType::JwtExpired(user) => write!(f, "JWT Expired for {:?}", user),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Error which should be converted into a HTTP response
|
|
#[derive(Debug)]
|
|
pub struct Error {
|
|
error_type: ErrorType,
|
|
inner: Option<Box<dyn std::error::Error + Send + Sync>>,
|
|
}
|
|
|
|
impl Error {
|
|
/// An unexpected error has occurred which prevented the request
|
|
/// from being handled properly..
|
|
pub fn new_unexpected(message: &str) -> Error {
|
|
error!("Unexpected error: {}", message);
|
|
Error {
|
|
error_type: ErrorType::InternalServerError,
|
|
inner: None,
|
|
}
|
|
}
|
|
|
|
/// Either authorization failed or the current user is not
|
|
/// authorized to perform the requested action
|
|
pub fn new_unauthorized() -> Error {
|
|
info!("Unauthorized user");
|
|
Error {
|
|
error_type: ErrorType::Unauthorized,
|
|
inner: None,
|
|
}
|
|
}
|
|
|
|
/// The user's JWT has expired and the they must log in again
|
|
pub fn new_jwt_expired(user: db::User) -> Error {
|
|
debug!("Jwt Expired");
|
|
Error {
|
|
error_type: ErrorType::JwtExpired(user),
|
|
inner: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<db::Error> for Error {
|
|
fn from(value: db::Error) -> Self {
|
|
Error {
|
|
error_type: ErrorType::InternalServerError,
|
|
inner: Some(Box::new(value)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<authentication::JwtError> for Error {
|
|
fn from(value: authentication::JwtError) -> Self {
|
|
error!(inner = value.to_string(), "Unhandled JWT error");
|
|
Error {
|
|
error_type: ErrorType::InternalServerError,
|
|
inner: Some(Box::new(value)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<authentication::AuthenticationError> for Error {
|
|
fn from(value: authentication::AuthenticationError) -> Self {
|
|
error!(inner = value.to_string(), "Error during authentication");
|
|
Error {
|
|
error_type: ErrorType::InternalServerError,
|
|
inner: Some(Box::new(value)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for Error {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Error {
|
|
error_type,
|
|
inner: Some(inner_err),
|
|
} => write!(f, "{}: {}", error_type, inner_err),
|
|
Error {
|
|
error_type,
|
|
inner: None,
|
|
} => write!(f, "{}", error_type),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for Error {
|
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
|
match &self.inner {
|
|
Some(inner) => Some(inner.as_ref()),
|
|
None => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Template)]
|
|
#[template(path = "error.html")]
|
|
struct ErrorTemplate {}
|
|
|
|
impl IntoResponse for Error {
|
|
fn into_response(self) -> Response {
|
|
match self.error_type {
|
|
ErrorType::InternalServerError => {
|
|
error!(inner = self.inner, "Uncaught error producing HTTP 500.");
|
|
(StatusCode::INTERNAL_SERVER_ERROR, ErrorTemplate {}).into_response()
|
|
}
|
|
ErrorType::Unauthorized => {
|
|
(StatusCode::UNAUTHORIZED, "User not authorized.").into_response()
|
|
}
|
|
ErrorType::JwtExpired(_) => {
|
|
todo!()
|
|
}
|
|
}
|
|
}
|
|
}
|