CompositeError, UserAuthRequired, /users/self & users/:id, misc

This commit is contained in:
2026-02-24 00:55:19 +01:00
parent 085764f06a
commit ee7ed48144
7 changed files with 164 additions and 19 deletions

View File

@@ -1,17 +1,23 @@
use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier, password_hash::SaltString};
use axum::http::{
HeaderMap,
header::{AUTHORIZATION, COOKIE},
use axum::{
http::{
HeaderMap, StatusCode,
header::{AUTHORIZATION, COOKIE},
},
response::{IntoResponse, Response},
};
use base64::{Engine, prelude::BASE64_STANDARD};
use rusqlite::OptionalExtension;
use uuid::Uuid;
use crate::{
database,
ISE_MSG, database,
users::{
User,
auth::{AuthError, COOKIE_NAME, TokenSize, UserAuthenticate, UserPasswordHashing},
auth::{
AuthError, COOKIE_NAME, TokenSize, UserAuthRequired, UserAuthenticate,
UserPasswordHashing,
},
sessions::Session,
},
};
@@ -27,6 +33,37 @@ impl TokenSize {
}
}
impl IntoResponse for AuthError {
fn into_response(self) -> Response {
match self {
Self::InvalidCredentials => (StatusCode::BAD_REQUEST, self.to_string()).into_response(),
Self::AuthRequired => (StatusCode::UNAUTHORIZED, self.to_string()).into_response(),
Self::SessionError(e) => e.into_response(),
Self::UserError(e) => e.into_response(),
Self::InvalidFormat => (StatusCode::BAD_REQUEST, self.to_string()).into_response(),
Self::InvalidBase64(_) => (StatusCode::BAD_REQUEST, self.to_string()).into_response(),
Self::InvalidUtf8(_) => (StatusCode::BAD_REQUEST, self.to_string()).into_response(),
Self::DatabaseError(e) => {
eprintln!("[ERROR] Database error occured: {e}");
(StatusCode::INTERNAL_SERVER_ERROR, ISE_MSG.to_string()).into_response()
}
Self::PassHashError(e) => {
eprintln!("[ERROR] A passwordhash error occured: {e}");
(StatusCode::INTERNAL_SERVER_ERROR, ISE_MSG.to_string()).into_response()
}
}
}
}
impl UserAuthRequired for Option<User> {
fn required(self) -> Result<User, AuthError> {
match self {
Self::None => Err(AuthError::AuthRequired),
Self::Some(u) => Ok(u),
}
}
}
impl UserPasswordHashing for User {
fn hash_password(passw: &str) -> Result<String, argon2::password_hash::Error> {
use rand08::rngs::OsRng as ArgonOsRng;

View File

@@ -10,6 +10,10 @@ pub const COOKIE_NAME: &str = "mnemohash";
pub trait UserAuthenticate {
fn authenticate(headers: &HeaderMap) -> Result<Option<User>, AuthError>;
}
pub trait UserAuthRequired {
fn required(self) -> Result<User, AuthError>;
}
pub trait UserPasswordHashing {
/// Returns the hashed password as a String
fn hash_password(passw: &str) -> Result<String, argon2::password_hash::Error>;
@@ -21,6 +25,8 @@ pub trait UserPasswordHashing {
pub enum AuthError {
#[error("Invalid credentials")]
InvalidCredentials,
#[error("Authentication required")]
AuthRequired,
#[error("Session error: {0}")]
SessionError(#[from] SessionError),
#[error("User error: {0}")]

View File

@@ -1,8 +1,13 @@
use axum::{
http::StatusCode,
response::{IntoResponse, Response},
};
use rusqlite::OptionalExtension;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::{
ISE_MSG,
database::{self},
users::{
auth::{AuthError, UserPasswordHashing},
@@ -34,16 +39,6 @@ pub enum UserError {
#[error("Argon2 passhash error: {0}")]
PassHashError(argon2::password_hash::Error),
}
impl From<rusqlite::Error> for UserError {
fn from(error: rusqlite::Error) -> Self {
UserError::DatabaseError(error.to_string())
}
}
impl From<argon2::password_hash::Error> for UserError {
fn from(err: argon2::password_hash::Error) -> Self {
UserError::PassHashError(err)
}
}
impl User {
pub fn get_by_id(id: Uuid) -> Result<User, UserError> {
@@ -176,3 +171,32 @@ impl User {
self.id == Uuid::nil()
}
}
impl From<rusqlite::Error> for UserError {
fn from(error: rusqlite::Error) -> Self {
UserError::DatabaseError(error.to_string())
}
}
impl From<argon2::password_hash::Error> for UserError {
fn from(err: argon2::password_hash::Error) -> Self {
UserError::PassHashError(err)
}
}
impl IntoResponse for UserError {
fn into_response(self) -> Response {
match self {
Self::DatabaseError(e) => {
eprintln!("[ERROR] Database error occured: {e}");
(StatusCode::INTERNAL_SERVER_ERROR, ISE_MSG.into())
}
Self::PassHashError(e) => {
eprintln!("[ERROR] A passwordhash error occured: {e}");
(StatusCode::INTERNAL_SERVER_ERROR, ISE_MSG.into())
}
Self::UserHandleError(_) => (StatusCode::BAD_REQUEST, self.to_string()),
Self::NoUserWithId(_) => (StatusCode::BAD_REQUEST, self.to_string()),
Self::NoUserWithHandle(_) => (StatusCode::BAD_REQUEST, self.to_string()),
}
.into_response()
}
}

View File

@@ -1,3 +1,7 @@
use axum::{
http::StatusCode,
response::{IntoResponse, Response},
};
use chrono::{DateTime, Duration, Utc};
use rusqlite::OptionalExtension;
use serde::{Deserialize, Serialize};
@@ -5,7 +9,7 @@ use sha2::{Digest, Sha256};
use uuid::Uuid;
use crate::{
database,
ISE_MSG, database,
users::{User, auth},
};
@@ -44,6 +48,19 @@ impl From<rusqlite::Error> for SessionError {
SessionError::DatabaseError(error.to_string())
}
}
impl IntoResponse for SessionError {
fn into_response(self) -> Response {
match self {
Self::DatabaseError(e) => {
eprintln!("[ERROR] Database error occured: {e}");
(StatusCode::INTERNAL_SERVER_ERROR, ISE_MSG.into())
}
Self::NoSessionWithId(_) => (StatusCode::BAD_REQUEST, self.to_string()),
Self::NoSessionWithToken(_) => (StatusCode::BAD_REQUEST, self.to_string()),
}
.into_response()
}
}
impl Session {
pub fn get_by_id(id: Uuid) -> Result<Session, SessionError> {