refactoring hexagonal and clean architecture

This commit is contained in:
Diocrafts
2026-02-08 13:40:23 +01:00
parent 3e4fb67c19
commit a82faa5eaf
101 changed files with 7433 additions and 9721 deletions
+49 -116
View File
@@ -22,7 +22,7 @@ pub struct AuthUser {
/// Se extrae automáticamente del `CurrentUser` insertado por el auth middleware.
///
/// Uso en handlers:
/// ```rust
/// ```ignore
/// async fn my_handler(CurrentUserId(user_id): CurrentUserId) -> impl IntoResponse { ... }
/// ```
#[derive(Clone, Debug)]
@@ -80,6 +80,9 @@ pub enum AuthError {
#[error("Acceso denegado: {0}")]
AccessDenied(String),
#[error("Servicio de autenticación no disponible")]
AuthServiceUnavailable,
}
impl IntoResponse for AuthError {
@@ -90,6 +93,7 @@ impl IntoResponse for AuthError {
AuthError::TokenExpired => (StatusCode::UNAUTHORIZED, "Token expirado".to_string()),
AuthError::UserNotFound => (StatusCode::UNAUTHORIZED, "Usuario no encontrado".to_string()),
AuthError::AccessDenied(msg) => (StatusCode::FORBIDDEN, msg),
AuthError::AuthServiceUnavailable => (StatusCode::INTERNAL_SERVER_ERROR, "Servicio de autenticación no disponible".to_string()),
};
let body = axum::Json(serde_json::json!({
@@ -100,146 +104,75 @@ impl IntoResponse for AuthError {
}
}
// Middleware de autenticación simplificado - solo valida si existe un token
/// Middleware de autenticación seguro.
///
/// Valida el token JWT contra el servicio de autenticación configurado.
/// No acepta bypasses, tokens mock, ni parámetros de URL para saltar validación.
pub async fn auth_middleware(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
mut request: Request,
next: Next,
) -> Result<Response, AuthError> {
// Check URL for special no_validation parameter to break auth loops
let uri = request.uri().to_string();
let skip_validation = uri.contains("no_redirect=true") || uri.contains("bypass_auth=true");
if skip_validation {
tracing::info!("Bypassing token validation due to special URL parameter");
// Create a default user for the request
let current_user = CurrentUser {
id: "default-user-id".to_string(),
username: "usuario".to_string(),
email: "usuario@example.com".to_string(),
role: "user".to_string(),
};
request.extensions_mut().insert(current_user);
return Ok(next.run(request).await);
}
// En una primera etapa, simplemente verificar si hay un token, sin validarlo
if let Some(token_str) = headers
// Extraer el token Bearer del header Authorization
let token_str = headers
.get(header::AUTHORIZATION)
.and_then(|value| value.to_str().ok())
.and_then(|value| value.strip_prefix("Bearer ")) {
// Handle mock tokens differently
let is_mock = token_str.contains("mock") || token_str == "mock_access_token";
if is_mock {
tracing::info!("Mock token detected, using simplified validation");
let current_user = CurrentUser {
id: "test-user-id".to_string(),
username: "test".to_string(),
email: "test@example.com".to_string(),
role: "user".to_string(),
};
request.extensions_mut().insert(current_user);
return Ok(next.run(request).await);
}
// Process normal token - try to validate it using JWT service
tracing::info!("Processing token: {}", token_str.chars().take(8).collect::<String>() + "...");
// Try to get the token service and validate the token
if let Some(auth_service) = state.auth_service.as_ref() {
let token_service = &auth_service.token_service;
match token_service.validate_token(token_str) {
Ok(claims) => {
tracing::info!("Token validated successfully for user: {}", claims.username);
let current_user = CurrentUser {
id: claims.sub,
username: claims.username,
email: claims.email,
role: claims.role,
};
request.extensions_mut().insert(current_user);
return Ok(next.run(request).await);
},
Err(e) => {
tracing::warn!("Token validation failed: {}", e);
return Err(AuthError::InvalidToken(format!("Token inválido: {}", e)));
}
}
}
// Fallback: if no auth service available, use token claims from parsing JWT manually
// Try to decode the token manually using jsonwebtoken
use jsonwebtoken::{decode, DecodingKey, Validation, Algorithm};
// Try with default secret (from environment or config)
let jwt_secret = std::env::var("JWT_SECRET").unwrap_or_else(|_| "oxicloud_secret_key_please_change_in_production".to_string());
#[derive(serde::Deserialize)]
struct Claims {
sub: String,
username: String,
email: String,
role: String,
}
let validation = Validation::new(Algorithm::HS256);
match decode::<Claims>(
token_str,
&DecodingKey::from_secret(jwt_secret.as_bytes()),
&validation
) {
Ok(token_data) => {
tracing::info!("Token decoded successfully for user: {}", token_data.claims.username);
.and_then(|value| value.strip_prefix("Bearer "))
.ok_or(AuthError::TokenNotProvided)?;
// Validar que el token no esté vacío
let token_str = token_str.trim();
if token_str.is_empty() {
return Err(AuthError::TokenNotProvided);
}
tracing::debug!("Processing authentication token");
// Validar el token usando el servicio de autenticación
if let Some(auth_service) = state.auth_service.as_ref() {
let token_service = &auth_service.token_service;
match token_service.validate_token(token_str) {
Ok(claims) => {
tracing::debug!("Token validated successfully for user: {}", claims.username);
let current_user = CurrentUser {
id: token_data.claims.sub,
username: token_data.claims.username,
email: token_data.claims.email,
role: token_data.claims.role,
id: claims.sub,
username: claims.username,
email: claims.email,
role: claims.role,
};
request.extensions_mut().insert(current_user);
return Ok(next.run(request).await);
},
Err(e) => {
tracing::warn!("Fallback token decode failed: {}", e);
tracing::warn!("Token validation failed: {}", e);
return Err(AuthError::InvalidToken(format!("Token inválido: {}", e)));
}
}
}
// Si hay un indicador para evitar redirección, permitir el acceso sin token
if uri.contains("api/") && uri.contains("login") {
tracing::info!("Allowing access to login endpoint without token");
return Ok(next.run(request).await);
}
// Si no hay token, devolver error de token no proporcionado
Err(AuthError::TokenNotProvided)
// Si no hay servicio de autenticación disponible, denegar acceso
tracing::error!("Auth middleware invoked but auth service is not configured");
Err(AuthError::AuthServiceUnavailable)
}
// Middleware simplificado para verificar roles de administrador
/// Middleware para verificar que el usuario autenticado tiene rol de administrador.
///
/// Debe aplicarse DESPUÉS del auth_middleware, ya que depende de que
/// `CurrentUser` esté presente en las extensiones de la request.
pub async fn require_admin(
headers: HeaderMap,
mut request: Request,
request: Request,
next: Next,
) -> Response {
// Implementación simplificada que verifica si hay un token de admin
if let Some(auth_value) = headers.get(header::AUTHORIZATION) {
if let Ok(auth_str) = auth_value.to_str() {
if auth_str.contains("admin") {
// Autorizado como admin
let current_user = CurrentUser {
id: "admin-user-id".to_string(),
username: "admin".to_string(),
email: "admin@example.com".to_string(),
role: "admin".to_string(),
};
request.extensions_mut().insert(current_user);
return next.run(request).await;
}
// Obtener el CurrentUser insertado por auth_middleware
if let Some(current_user) = request.extensions().get::<CurrentUser>() {
if current_user.role == "admin" {
tracing::debug!("Admin access granted for user: {}", current_user.username);
return next.run(request).await;
}
tracing::warn!("Admin access denied for user: {} (role: {})", current_user.username, current_user.role);
} else {
tracing::warn!("Admin check failed: no authenticated user in request");
}
// Acceso denegado