from functools import wraps from flask import render_template, abort, current_app from flask_login import current_user def permission_required(min_level): """ Decorador para verificar nivel de permisos en rutas web. Args: min_level (int): Nivel mínimo requerido Returns: function: Decorador configurado """ def decorator(f): @wraps(f) def decorated_function(*args, **kwargs): if not current_user.is_authenticated: return current_app.login_manager.unauthorized() if not current_user.has_permission(min_level): return render_template('error.html', error_code=403, error_message="No tiene permisos suficientes para acceder a esta página."), 403 return f(*args, **kwargs) return decorated_function return decorator def project_access_required(access_type='view'): """ Decorador para verificar acceso a un proyecto específico. Args: access_type (str): Tipo de acceso ('view' o 'edit') Returns: function: Decorador configurado """ def decorator(f): @wraps(f) def decorated_function(*args, **kwargs): if not current_user.is_authenticated: return current_app.login_manager.unauthorized() # Obtener ID del proyecto de los argumentos project_id = kwargs.get('project_id') if not project_id: abort(400, description="ID de proyecto no proporcionado") # Verificar permisos específicos del proyecto from services.project_service import get_project project = get_project(project_id) if not project: abort(404, description="Proyecto no encontrado") # Verificar si el proyecto está activo if project.get('estado') != 'activo' and not current_user.has_permission(9000): return render_template('error.html', error_code=403, error_message="El proyecto no está activo."), 403 # Verificar permisos específicos # Administradores siempre tienen acceso if current_user.has_permission(9000): return f(*args, **kwargs) # Verificar permisos en el archivo de permisos del proyecto from utils.file_utils import load_json_file import os storage_path = current_app.config['STORAGE_PATH'] project_dir = os.path.join(storage_path, 'projects', project.get('directory', '')) permissions_file = os.path.join(project_dir, 'permissions.json') permissions = load_json_file(permissions_file, {}) # Verificar permisos específicos del usuario user_permissions = permissions.get(current_user.id, {}) if access_type == 'edit' and user_permissions.get('can_edit', False): return f(*args, **kwargs) if access_type == 'view' and user_permissions.get('can_view', False): return f(*args, **kwargs) # Verificar nivel general requerido según el esquema schema_code = project.get('esquema') if not schema_code: return render_template('error.html', error_code=403, error_message="No tiene permisos para acceder a este proyecto."), 403 from services.schema_service import get_schema schema = get_schema(schema_code) if not schema: return render_template('error.html', error_code=403, error_message="Esquema no encontrado."), 403 # Nivel mínimo para ver/editar según el esquema min_level_view = 0 # Por defecto, cualquier usuario autenticado puede ver min_level_edit = 5000 # Por defecto, se requiere nivel de gestor para editar # Si hay configuración específica en el esquema, usarla if 'nivel_ver' in schema: min_level_view = schema.get('nivel_ver', 0) if 'nivel_editar' in schema: min_level_edit = schema.get('nivel_editar', 5000) # Verificar nivel según tipo de acceso if access_type == 'edit' and current_user.has_permission(min_level_edit): return f(*args, **kwargs) if access_type == 'view' and current_user.has_permission(min_level_view): return f(*args, **kwargs) # Si llega aquí, no tiene permisos return render_template('error.html', error_code=403, error_message="No tiene permisos para acceder a este proyecto."), 403 return decorated_function return decorator def document_access_required(access_type='view'): """ Decorador para verificar acceso a un documento específico. Args: access_type (str): Tipo de acceso ('view' o 'edit') Returns: function: Decorador configurado """ def decorator(f): @wraps(f) def decorated_function(*args, **kwargs): if not current_user.is_authenticated: return current_app.login_manager.unauthorized() # Obtener IDs de proyecto y documento project_id = kwargs.get('project_id') document_id = kwargs.get('document_id') if not project_id or not document_id: abort(400, description="ID de proyecto o documento no proporcionado") # Verificar permisos del proyecto primero from services.project_service import get_project project = get_project(project_id) if not project: abort(404, description="Proyecto no encontrado") # Verificar si el proyecto está activo if project.get('estado') != 'activo' and not current_user.has_permission(9000): return render_template('error.html', error_code=403, error_message="El proyecto no está activo."), 403 # Administradores siempre tienen acceso if current_user.has_permission(9000): return f(*args, **kwargs) # Obtener información del documento from services.document_service import get_document document = get_document(project_id, document_id) if not document: abort(404, description="Documento no encontrado") # Obtener el esquema del proyecto schema_code = project.get('esquema') if not schema_code: return render_template('error.html', error_code=403, error_message="No tiene permisos para acceder a este documento."), 403 # Cargar el esquema específico del proyecto from utils.file_utils import load_json_file import os storage_path = current_app.config['STORAGE_PATH'] project_dir = os.path.join(storage_path, 'projects', project.get('directory', '')) project_schema_file = os.path.join(project_dir, 'schema.json') schema = load_json_file(project_schema_file) if not schema: # Si no hay esquema específico, usar el general from services.schema_service import get_schema schema = get_schema(schema_code) if not schema: return render_template('error.html', error_code=403, error_message="Esquema no encontrado."), 403 # Obtener el tipo de documento document_name = document.get('document_id', '').split('_', 1)[1] if '_' in document.get('document_id', '') else '' # Buscar configuración de permisos para este tipo de documento for doc_type in schema.get('documentos', []): if doc_type.get('nombre', '').lower().replace(' ', '_') == document_name: # Verificar nivel según tipo de acceso if access_type == 'edit' and current_user.has_permission(doc_type.get('nivel_editar', 5000)): return f(*args, **kwargs) if access_type == 'view' and current_user.has_permission(doc_type.get('nivel_ver', 0)): return f(*args, **kwargs) # Si no se encontró configuración específica o no tiene permisos return render_template('error.html', error_code=403, error_message="No tiene permisos para acceder a este documento."), 403 return decorated_function return decorator