46 lines
1.9 KiB
Python
46 lines
1.9 KiB
Python
from functools import wraps
|
|
from flask import request, jsonify, redirect, url_for, flash, abort, g
|
|
from flask_login import current_user
|
|
|
|
def require_permission(module: str, min_level: str = 'read'):
|
|
"""
|
|
Decorador de seguridad declarativa a nivel de método inspirado en @PreAuthorize de Spring Security.
|
|
Verifica los privilegios del usuario autenticado (tanto en sesiones web como en API JWT).
|
|
|
|
Niveles de permiso estándar:
|
|
- 'read': Permiso de lectura / consulta
|
|
- 'read_write': Permiso de modificación y creación
|
|
- 'admin': Permiso de control total
|
|
"""
|
|
def decorator(f):
|
|
@wraps(f)
|
|
def decorated_function(*args, **kwargs):
|
|
user = None
|
|
# Evaluar contexto JWT primero si está presente
|
|
if hasattr(g, 'jwt_user') and g.jwt_user:
|
|
user = g.jwt_user
|
|
elif current_user and current_user.is_authenticated:
|
|
user = current_user
|
|
|
|
is_api = request.path.startswith('/api/') or request.is_json
|
|
|
|
if not user:
|
|
if is_api:
|
|
return jsonify({'error': 'Unauthorized', 'message': 'Autenticación requerida.'}), 401
|
|
flash('Debe iniciar sesión para acceder a este recurso.', 'warning')
|
|
return redirect(url_for('auth.login', next=request.url))
|
|
|
|
# Comprobar privilegios en la matriz RBAC
|
|
if not user.has_permission(module, min_level):
|
|
if is_api:
|
|
return jsonify({
|
|
'error': 'Forbidden',
|
|
'message': f'Permiso insuficiente en módulo "{module}". Nivel requerido: "{min_level}".'
|
|
}), 403
|
|
flash(f'No posee permisos suficientes ({min_level}) para gestionar {module}.', 'danger')
|
|
abort(403)
|
|
|
|
return f(*args, **kwargs)
|
|
return decorated_function
|
|
return decorator
|