use std::fmt; #[derive(Debug)] pub enum AuthError { BadRequest(String), InvalidCredentials, Unauthorized, Forbidden, NotFound, Conflict(String), RateLimited, Internal(String), TwoFactorExpired, TwoFactorCodeInvalid, RecoveryCodeInvalid, VerificationCodeExpired, VerificationCodeInvalid, ResetTokenInvalid, } impl fmt::Display for AuthError { 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 => write!(f, "rate limit exceeded"), Self::Internal(message) => write!(f, "{message}"), Self::TwoFactorExpired => write!(f, "two-factor challenge expired"), Self::TwoFactorCodeInvalid => write!(f, "invalid two-factor code"), Self::RecoveryCodeInvalid => write!(f, "invalid or used recovery code"), Self::VerificationCodeExpired => write!(f, "verification code expired"), Self::VerificationCodeInvalid => write!(f, "invalid verification code"), Self::ResetTokenInvalid => write!(f, "invalid or expired reset token"), } } } impl std::error::Error for AuthError {} impl From for AuthError { fn from(error: deletion::DeletionError) -> Self { use deletion::DeletionError; match error { DeletionError::Database(error) => Self::from(error), DeletionError::Conflict(message) => Self::Conflict(message), DeletionError::NotFound(message) => { tracing::debug!(%message, "deletion target not found"); Self::NotFound } DeletionError::KenAuth(message) | DeletionError::KenShell(message) | DeletionError::Config(message) => Self::Internal(message), } } } export::db_error_conversions!(AuthError); export::redis_error_conversion!(AuthError); export::export_error_conversion!(AuthError); export::json_payload_conversion!(AuthError); export::api_error! { log: "auth", pub enum ApiError { BadRequest(400, "request failed validation"), InvalidCredentials(400, "credentials rejected"), 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 From for ApiError { fn from(error: AuthError) -> Self { match error { AuthError::BadRequest(msg) => Self::bad_request(msg), AuthError::InvalidCredentials => { Self::build(400, Self::payload(400, "invalid credentials")) } AuthError::Unauthorized => Self::unauthorized(), AuthError::Forbidden => Self::forbidden(), AuthError::NotFound => Self::not_found("auth resource not found"), AuthError::Conflict(msg) => Self::conflict(msg), AuthError::RateLimited => Self::rate_limited_exceeded(), AuthError::TwoFactorExpired => Self::build( 400, Self::payload(400, "two-factor challenge expired") .with_error_code("two_factor_expired"), ), AuthError::TwoFactorCodeInvalid => Self::build( 400, Self::payload(400, "invalid two-factor code") .with_error_code("two_factor_code_invalid"), ), AuthError::RecoveryCodeInvalid => Self::build( 400, Self::payload(400, "invalid or used recovery code") .with_error_code("recovery_code_invalid"), ), AuthError::VerificationCodeExpired => Self::build( 400, Self::payload(400, "verification code expired") .with_error_code("verification_code_expired"), ), AuthError::VerificationCodeInvalid => Self::build( 400, Self::payload(400, "invalid verification code") .with_error_code("verification_code_invalid"), ), AuthError::ResetTokenInvalid => Self::build( 400, Self::payload(400, "invalid or expired reset token") .with_error_code("reset_token_invalid"), ), AuthError::Internal(message) => Self::internal(message), } } }