Estructurar permisos en un SaaS – Implementando roles en Flask - Episodio 25

roles-en-flask

Estucturar permisos en un SaaS es importante.

De hecho, es clave!

Por eso, hoy vengo a hablar sobre cómo implenté roles en Flask.

Índice
  1. 🚀 ¿Por qué necesitamos un sistema de roles en nuestra API? 📅 Diario de un SaaS
  2. 🔍 Definiendo los roles del sistema
  3. 🛠️ Implementando roles en Flask con JWT y middleware
    1. ✅ 1. Agregar un campo role a la base de datos
    2. ✅ 2. Modificar el registro de usuarios para asignar roles
    3. ✅ 3. Middleware para restringir accesos por rol
    4. ✅ 4. Proteger rutas según el rol
  4. ❌ Errores encontrados y cómo los solucioné
    1. 🔴 AttributeError: 'NoneType' object is not subscriptable en get_jwt_identity()
    2. 🔴 403 Forbidden en rutas protegidas para usuarios con permisos correctos
  5. 💡 Aprendizajes del proceso
    1. 🔹 Definir roles desde el inicio evita problemas en el futuro
    2. 🔹 Un middleware bien diseñado simplifica la gestión de permisos
    3. 🔹 JWT + roles es una combinación poderosa para la seguridad
  6. 🚀 Próximos pasos

🚀 ¿Por qué necesitamos un sistema de roles en nuestra API? 📅 Diario de un SaaS

A medida que mi proyecto avanzaba, me encontré con un problema evidente: no todos los usuarios deben tener los mismos permisos.

La plataforma necesitaba un sistema de acceso estructurado, porque sin él:

🔴 Cualquiera podría modificar datos sensibles.
🔴 No habría control sobre las acciones dentro del sistema.
🔴 La seguridad y la escalabilidad se verían comprometidas.

Era obvio que necesitaba implementar roles y permisos en Flask.

Así que me puse manos a la obra.

En este post te cuento cómo lo hice paso a paso, los errores que encontré y cómo los solucioné. 🔥


🔍 Definiendo los roles del sistema

Antes de meterme con el código, decidí que lo primero era definir claramente los roles.

Porque claro, un sistema sin una estructura bien pensada es un desastre en potencia.

Así que tracé estos tres perfiles principales:

1️⃣ Admin → Tiene control total. Puede gestionar usuarios, modificar registros y acceder a todas las funciones del sistema.

2️⃣ Editor → Puede gestionar su propio contenido, modificar registros que le pertenezcan y ver estadísticas.

3️⃣ Usuario estándar → Solo puede consultar información, sin permisos para realizar cambios.

Con esto claro, pasé a la parte técnica.

Es decir, me bajé al barro a ensuciarme lo máximo!

Así que aposté por Flask-JWT-Extended sería mi herramienta clave para la autenticación...

Y no solo eso, sino que necesitaría una forma de validar los permisos en cada endpoint.


🛠️ Implementando roles en Flask con JWT y middleware

Para que cada usuario tuviera un rol asignado, primero agregué un campo role en la base de datos.

✅ 1. Agregar un campo role a la base de datos

Ejecuté esta migración en PostgreSQL:

ALTER TABLE users ADD COLUMN role VARCHAR(20) DEFAULT 'usuario';

De esta manera, cualquier nuevo usuario registrado tendría el rol de usuario por defecto, salvo que se le asignara otro rol manualmente.


✅ 2. Modificar el registro de usuarios para asignar roles

Luego, modifiqué el endpoint de registro para permitir la asignación de roles de manera segura.

@app.route('/register', methods=['POST'])
def register():
    data = request.get_json()
    username = data.get('username')
    password = generate_password_hash(data.get('password'))
    role = data.get('role', 'usuario')  # Por defecto, asigna el rol de usuario
    
    cursor.execute("INSERT INTO users (username, password, role) VALUES (%s, %s, %s)", (username, password, role))
    db.commit()
    
    return jsonify({'message': 'Usuario registrado correctamente'}), 201

Con esto, ya podía registrar usuarios con distintos roles, pero faltaba lo más importante: controlar los accesos a las rutas de la API según el rol asignado.


✅ 3. Middleware para restringir accesos por rol

Aquí es donde entra en juego Flask-JWT-Extended. Creé un decorador para validar roles antes de acceder a los endpoints:

from flask_jwt_extended import get_jwt_identity, verify_jwt_in_request
from functools import wraps

# Middleware para verificar roles
def role_required(required_role):
    def decorator(f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            verify_jwt_in_request()
            current_user = get_jwt_identity()
            
            cursor.execute("SELECT role FROM users WHERE username = %s", (current_user,))
            user_role = cursor.fetchone()[0]
            
            if user_role != required_role:
                return jsonify({'error': 'No tienes permiso para acceder a este recurso'}), 403
            
            return f(*args, **kwargs)
        return wrapper
    return decorator

Este decorador se usa en las rutas que requieren permisos específicos.

Por lo que si un usuario intenta acceder a una ruta sin el rol adecuado, recibe un error 403 y se le deniega el acceso.


✅ 4. Proteger rutas según el rol

Ahora, con el middleware en su lugar, proteger las rutas fue tan simple como aplicar el decorador @role_required():

@app.route('/admin/dashboard', methods=['GET'])
@role_required('admin')
def admin_dashboard():
    return jsonify({'message': 'Bienvenido al panel de administración'}), 200

@app.route('/editor/publicaciones', methods=['POST'])
@role_required('editor')
def crear_publicacion():
    return jsonify({'message': 'Publicación creada exitosamente'}), 201

Con esto:

Los administradores tienen control total.
Los editores pueden gestionar sus propios contenidos.
Los usuarios estándar tienen acceso limitado.


❌ Errores encontrados y cómo los solucioné

🔴 AttributeError: 'NoneType' object is not subscriptable en get_jwt_identity()

Solución:

  • Asegurar que el token JWT se envía en la cabecera de la solicitud.
  • Usar verify_jwt_in_request() antes de get_jwt_identity() para evitar errores.

🔴 403 Forbidden en rutas protegidas para usuarios con permisos correctos

Solución:

  • Verificar que el campo role en la base de datos está correctamente asignado.
  • Revisar que el decorador @role_required está llamando correctamente la función protegida.

roles-en-flask-permisos


💡 Aprendizajes del proceso

🔹 Definir roles desde el inicio evita problemas en el futuro

Si dejas esto para después, tendrás que reestructurar toda tu lógica de negocio cuando crezca la aplicación.

🔹 Un middleware bien diseñado simplifica la gestión de permisos

Con un decorador como @role_required, controlar accesos es más limpio y escalable.

🔹 JWT + roles es una combinación poderosa para la seguridad

El uso de tokens JWT combinados con roles en la base de datos proporciona una autenticación robusta y segura.


🚀 Próximos pasos

Con los roles funcionando, el siguiente paso es optimizar el rendimiento del backend y prepararlo para escalar sin problemas. 🔥

➡️ ¿Cómo gestionas roles y permisos en tu SaaS?

¿Has tenido problemas con autenticación en Flask?

Cuéntamelo en los comentarios.

Si quieres conocer otros artículos parecidos a Estructurar permisos en un SaaS – Implementando roles en Flask - Episodio 25 puedes visitar la categoría Proyecto-IA.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir