Autorización básica con decoradores

Intermedio
Flask
Flask
Actualizado: 20/06/2025

¡Desbloquea el curso de Flask completo!

IA
Ejercicios
Certificado
Entrar

Mira la lección en vídeo

Accede al vídeo completo de esta lección y a más contenido exclusivo con el Plan Plus.

Desbloquear Plan Plus

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

Guarda tu progreso

Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.

Progreso guardado
Asistente IA
Ejercicios
Iniciar sesión gratis

Más de 25.000 desarrolladores ya confían en CertiDevs

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.

Aprendizajes de esta lección de Flask

  • 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.

Completa este curso de Flask y certifícate

Únete a nuestra plataforma de cursos de programación y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.

Asistente IA

Resuelve dudas al instante

Ejercicios

Practica con proyectos reales

Certificados

Valida tus conocimientos

Más de 25.000 desarrolladores ya se han certificado con CertiDevs

⭐⭐⭐⭐⭐
4.9/5 valoración