Control de acceso por roles
El control de acceso por roles permite restringir el acceso a diferentes partes de una aplicación según el tipo de usuario. En Flask, podemos implementar este sistema utilizando decoradores personalizados que verifican los permisos del usuario autenticado antes de ejecutar una vista.
Para implementar un sistema básico de roles, necesitamos primero definir los roles disponibles en nuestra aplicación. Podemos hacerlo mediante una enumeración o constantes:
from enum import Enum
class Role(Enum):
ADMIN = "admin"
MODERATOR = "moderator"
USER = "user"
Nuestro modelo de usuario debe incluir un campo para almacenar el rol asignado:
from flask_sqlalchemy import SQLAlchemy
from flask_login import UserMixin
db = SQLAlchemy()
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password_hash = db.Column(db.String(128))
role = db.Column(db.String(20), default=Role.USER.value)
def has_role(self, role):
"""Verifica si el usuario tiene un rol específico"""
return self.role == role.value
def is_admin(self):
"""Método de conveniencia para verificar si es administrador"""
return self.role == Role.ADMIN.value
Creación del decorador de autorización
El decorador de autorización es el componente central que verifica si un usuario tiene los permisos necesarios:
from functools import wraps
from flask import abort
from flask_login import current_user
def require_role(required_role):
"""
Decorador que requiere un rol específico para acceder a una vista
"""
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
# Verificar si el usuario está autenticado
if not current_user.is_authenticated:
abort(401) # No autorizado
# Verificar si tiene el rol requerido
if not current_user.has_role(required_role):
abort(403) # Prohibido
return f(*args, **kwargs)
return decorated_function
return decorator
También podemos crear un decorador más flexible que acepte múltiples roles:
def require_roles(*allowed_roles):
"""
Decorador que permite acceso a usuarios con cualquiera de los roles especificados
"""
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if not current_user.is_authenticated:
abort(401)
# Verificar si el usuario tiene alguno de los roles permitidos
user_has_permission = any(
current_user.has_role(role) for role in allowed_roles
)
if not user_has_permission:
abort(403)
return f(*args, **kwargs)
return decorated_function
return decorator
Aplicación práctica en rutas
Una vez definidos los decoradores, podemos aplicarlos a nuestras rutas protegidas:
from flask import Flask, render_template
from flask_login import login_required
app = Flask(__name__)
@app.route('/admin/dashboard')
@login_required
@require_role(Role.ADMIN)
def admin_dashboard():
"""Panel de administración - solo para administradores"""
return render_template('admin/dashboard.html')
@app.route('/moderate/posts')
@login_required
@require_roles(Role.ADMIN, Role.MODERATOR)
def moderate_posts():
"""Moderación de posts - para admins y moderadores"""
posts = Post.query.filter_by(status='pending').all()
return render_template('moderate/posts.html', posts=posts)
@app.route('/user/profile')
@login_required
def user_profile():
"""Perfil de usuario - para cualquier usuario autenticado"""
return render_template('user/profile.html')
Sistema de jerarquía de roles
Para aplicaciones más complejas, podemos implementar un sistema jerárquico donde ciertos roles incluyen los permisos de roles inferiores:
class RoleHierarchy:
"""Define la jerarquía de roles y sus permisos"""
HIERARCHY = {
Role.ADMIN: [Role.ADMIN, Role.MODERATOR, Role.USER],
Role.MODERATOR: [Role.MODERATOR, Role.USER],
Role.USER: [Role.USER]
}
@classmethod
def has_permission(cls, user_role, required_role):
"""Verifica si un rol tiene permisos para realizar una acción"""
user_role_enum = Role(user_role)
allowed_roles = cls.HIERARCHY.get(user_role_enum, [])
return required_role in allowed_roles
def require_role_hierarchical(required_role):
"""
Decorador que considera la jerarquía de roles
"""
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if not current_user.is_authenticated:
abort(401)
if not RoleHierarchy.has_permission(current_user.role, required_role):
abort(403)
return f(*args, **kwargs)
return decorated_function
return decorator
Manejo de errores de autorización
Es importante proporcionar páginas de error personalizadas para mejorar la experiencia del usuario:
@app.errorhandler(403)
def forbidden(error):
"""Página de error para acceso prohibido"""
return render_template('errors/403.html'), 403
@app.errorhandler(401)
def unauthorized(error):
"""Página de error para usuarios no autenticados"""
return render_template('errors/401.html'), 401
En las plantillas Jinja, podemos mostrar contenido condicional según el rol:
<!-- En la plantilla base.html -->
{% if current_user.is_authenticated %}
{% if current_user.is_admin() %}
<a href="{{ url_for('admin_dashboard') }}">Panel Admin</a>
{% endif %}
{% if current_user.role in ['admin', 'moderator'] %}
<a href="{{ url_for('moderate_posts') }}">Moderar Posts</a>
{% endif %}
{% endif %}
Este enfoque proporciona un control de acceso granular y escalable, permitiendo que la aplicación crezca manteniendo la seguridad y organización del código.
Fuentes y referencias
Documentación oficial y recursos externos para profundizar en Flask
Documentación oficial de Flask
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Flask es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de Flask
Explora más contenido relacionado con Flask y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
- Comprender el concepto de control de acceso por roles en aplicaciones web.
- Definir roles y asignarlos a usuarios en un modelo de datos.
- Crear decoradores personalizados para verificar permisos antes de acceder a rutas.
- Implementar un sistema jerárquico de roles para permisos escalables.
- Manejar errores de autorización y mostrar contenido condicional en plantillas según el rol.