Setup a proper error type for the web application
This commit is contained in:
parent
0782e82d6e
commit
22761ee92e
46
src/error.rs
46
src/error.rs
|
@ -1,25 +1,41 @@
|
|||
use core::fmt;
|
||||
|
||||
use axum::{
|
||||
http::StatusCode,
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
pub struct AppError(anyhow::Error);
|
||||
#[derive(Error, Debug)]
|
||||
pub enum AppError {
|
||||
InvalidCsrf(#[from] axum_csrf::CsrfError),
|
||||
Other(#[from] anyhow::Error),
|
||||
}
|
||||
|
||||
impl AppError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match self {
|
||||
AppError::Other(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
AppError::InvalidCsrf(_) => StatusCode::BAD_REQUEST,
|
||||
}
|
||||
}
|
||||
|
||||
fn message(&self) -> String {
|
||||
match self {
|
||||
AppError::Other(e) => format!("something went wrong: {}", e),
|
||||
AppError::InvalidCsrf(e) => format!("unable to verify csrf: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for AppError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.message())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoResponse for AppError {
|
||||
fn into_response(self) -> Response {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Something went wrong: {}", self.0),
|
||||
)
|
||||
.into_response()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> From<E> for AppError
|
||||
where
|
||||
E: Into<anyhow::Error>,
|
||||
{
|
||||
fn from(err: E) -> Self {
|
||||
Self(err.into())
|
||||
(self.status_code(), self.message()).into_response()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ struct Register {
|
|||
}
|
||||
|
||||
impl<'a> TryInto<NewUser<'a>> for &'a Register {
|
||||
type Error = argon2::password_hash::Error;
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_into(self: &'a Register) -> Result<NewUser<'a>, Self::Error> {
|
||||
let salt = SaltString::generate(&mut OsRng);
|
||||
|
@ -86,18 +86,11 @@ impl<'a> TryInto<NewUser<'a>> for &'a Register {
|
|||
}
|
||||
|
||||
async fn register(
|
||||
csrf_token: CsrfToken,
|
||||
c: CsrfToken,
|
||||
Form(register): Form<Register>,
|
||||
) -> Result<impl IntoResponse, AppError> {
|
||||
// TODO: https://docs.rs/axum_csrf/latest/axum_csrf/#prevent-post-replay-attacks-with-csrf
|
||||
|
||||
let v = csrf_token.verify(®ister.authenticity_token);
|
||||
if v.is_err() {
|
||||
return Ok((
|
||||
StatusCode::BAD_REQUEST,
|
||||
Html(html! { "invalid request" }.into_string()),
|
||||
));
|
||||
}
|
||||
c.verify(®ister.authenticity_token)?;
|
||||
|
||||
let new_user: NewUser = (®ister).try_into()?;
|
||||
|
||||
|
|
34
src/views.rs
34
src/views.rs
|
@ -12,6 +12,14 @@ use crate::{
|
|||
partials::{footer, header},
|
||||
};
|
||||
|
||||
pub async fn csrf<F>(csrf: CsrfToken, cb: F) -> impl IntoResponse
|
||||
where
|
||||
F: Fn(&str) -> Html<String>,
|
||||
{
|
||||
let token = csrf.authenticity_token().unwrap();
|
||||
(csrf, cb(&token))
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
pub async fn index() -> Html<String> {
|
||||
Html(
|
||||
|
@ -39,10 +47,8 @@ pub async fn index() -> Html<String> {
|
|||
)
|
||||
}
|
||||
|
||||
pub async fn register(csrf: CsrfToken) -> impl IntoResponse {
|
||||
let token = csrf.authenticity_token().unwrap();
|
||||
(
|
||||
csrf,
|
||||
pub async fn register(t: CsrfToken) -> impl IntoResponse {
|
||||
csrf(t, |token| {
|
||||
Html(
|
||||
html! {
|
||||
(header())
|
||||
|
@ -64,22 +70,20 @@ pub async fn register(csrf: CsrfToken) -> impl IntoResponse {
|
|||
(footer())
|
||||
}
|
||||
.into_string(),
|
||||
),
|
||||
)
|
||||
.into_response()
|
||||
)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn login(csrf: CsrfToken) -> impl IntoResponse {
|
||||
let token = csrf.authenticity_token().unwrap();
|
||||
(
|
||||
csrf,
|
||||
pub async fn login(t: CsrfToken) -> impl IntoResponse {
|
||||
csrf(t, |token| {
|
||||
Html(
|
||||
html! {
|
||||
(header())
|
||||
main class="prose" {
|
||||
h1 { "Login" }
|
||||
form method="post" {
|
||||
input type="hidden" name="authenticity_token" value=(token) {}
|
||||
input type="hidden" name="authenticity_token" value=(token) {}
|
||||
label {
|
||||
input {}
|
||||
}
|
||||
|
@ -91,9 +95,9 @@ pub async fn login(csrf: CsrfToken) -> impl IntoResponse {
|
|||
(footer())
|
||||
}
|
||||
.into_string(),
|
||||
),
|
||||
)
|
||||
.into_response()
|
||||
)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
|
|
Loading…
Reference in a new issue