use std::fmt; use axum::response::{IntoResponse, Response}; use export::rest::AppResponseData; use export::rest::NoData; #[derive(Debug)] pub enum AdminError { BadRequest(String), InvalidCredentials, Unauthorized, Forbidden, NotFound, Conflict(String), RateLimited(String), Internal(String), } impl fmt::Display for AdminError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::BadRequest(message) => write!(f, "{message}"), Self::InvalidCredentials => write!(f, "invalid credentials"), Self::Unauthorized => write!(f, "unauthorized"), Self::Forbidden => write!(f, "forbidden"), Self::NotFound => write!(f, "not found"), Self::Conflict(message) => write!(f, "{message}"), Self::RateLimited(message) => write!(f, "{message}"), Self::Internal(message) => write!(f, "{message}"), } } } impl std::error::Error for AdminError {} export::db_error_conversions!(AdminError); export::storage_error_conversion!(AdminError); export::redis_error_conversion!(AdminError); export::export_error_conversion!(AdminError); export::json_payload_conversion!(AdminError); impl From for AdminError { fn from(error: auth::errors::AuthError) -> Self { match error { auth::errors::AuthError::BadRequest(message) => Self::BadRequest(message), auth::errors::AuthError::InvalidCredentials => Self::InvalidCredentials, auth::errors::AuthError::Unauthorized => Self::Unauthorized, auth::errors::AuthError::Forbidden => Self::Forbidden, auth::errors::AuthError::NotFound => Self::NotFound, auth::errors::AuthError::Conflict(message) => Self::Conflict(message), auth::errors::AuthError::RateLimited => { Self::RateLimited("rate limit exceeded".to_string()) } auth::errors::AuthError::PowFailed => { Self::BadRequest("proof of work failed".to_string()) } auth::errors::AuthError::TwoFactorExpired => { Self::BadRequest("two-factor challenge expired".to_string()) } auth::errors::AuthError::TwoFactorCodeInvalid => { Self::BadRequest("invalid two-factor code".to_string()) } auth::errors::AuthError::RecoveryCodeInvalid => { Self::BadRequest("invalid or used recovery code".to_string()) } auth::errors::AuthError::VerificationCodeExpired => { Self::BadRequest("verification code expired".to_string()) } auth::errors::AuthError::VerificationCodeInvalid => { Self::BadRequest("invalid verification code".to_string()) } auth::errors::AuthError::ResetTokenInvalid => { Self::BadRequest("invalid or expired reset token".to_string()) } auth::errors::AuthError::Internal(message) => Self::Internal(message), } } } export::api_error! { log: "admin", pub enum AdminApiError { BadRequest(400, "request failed validation"), Unauthorized(401, "authentication required"), Forbidden(403, "operation not allowed"), NotFound(404, "resource not found"), Conflict(409, "resource conflict"), RateLimited(429, "rate limit exceeded"), Internal(500, "internal server error"), } } impl AdminApiError { pub fn invalid_credentials() -> Self { Self::build( 401, AppResponseData::::err(401, "invalid username or password") .with_error_code("invalid_credentials"), ) } pub fn password_change_required() -> Self { Self::build( 403, AppResponseData::::err(403, "password change required") .with_error_code("password_change_required"), ) } } pub fn password_change_required_response() -> Response { AdminApiError::password_change_required().into_response() } impl From for AdminApiError { fn from(error: AdminError) -> Self { match error { AdminError::BadRequest(message) => Self::bad_request(message), AdminError::InvalidCredentials => Self::invalid_credentials(), AdminError::Unauthorized => Self::unauthorized(), AdminError::Forbidden => Self::forbidden(), AdminError::NotFound => Self::not_found("admin resource not found"), AdminError::Conflict(message) => Self::conflict(message), AdminError::RateLimited(message) => Self::rate_limited(message), AdminError::Internal(message) => Self::internal(message), } } }