Identificando y registrando con Flask-Security

Cuando comenzamos a crear un MVP (minimum viable product o producto mínimo viable), es crítico centrarse en aquellas características que aportan valor. Lo ideal sería no perder un minuto en nada que no tenga que ver con la esencia de la idea que queremos ejecutar. Sin embargo hay ocasiones que, para poder liberar nuestro MVP, debemos implementar características que no van proporcionar feedback alguno pero condicionan la viabilidad del producto.

En el caso de una aplicación multiusuario que esté disponible en la nube necesitaremos registrar e identificar a los usuarios. Es una restricción que no aporta valor al potencial cliente interesado en nuestro producto, pero por lo general lo necesitamos si nuestra aplicación está abierta al público. Aunque el registro y la identificación de usuarios pueden parecer una tarea trivial, hay que tener en cuenta algunas consideraciones:

  • Tiene que ser un proceso fácil de seguir: aunque no aporte valor al producto, podría convertirse en una barrera para que los usuarios interesados lleguen a probar el MVP.
  • El registro e identificación llevan implícitas muchas tareas no triviales: Encriptación de cookies, encriptación de contraseñas, envío de correo de confirmación, proceso de recuperación de contraseña, generación de tokens, gestión de permisos,…

 

Existe una extensión de Flask llamada Flask-Security, que propone un proceso de registro, implementa una buena parte de las necesidades referentes a seguridad para aplicaciones web y puede ser integrada y configurada en unas pocas horas. En su página presenta una aplicación mínima que nosotros vamos a extender ligeramente para profundizar un poco más en lo que podría ofrecer para nuestros productos.

Antes de nada, deberíamos establecer nuestro entorno tal y como se explicó en Desarrollando con Flask. Una vez creado el entorno, las dependencias en este caso se instalarían usando:

(env)$ pip install flask-security flask-sqlalchemy flask-mail

El código del ejemplo modificado lo guardaríamos en un fichero app.py de la siguiente manera:

# -*- coding: utf-8 -*-

import os

import datetime

from flask import Flask, render_template

from flask.ext.sqlalchemy import SQLAlchemy

from flask.ext.security import Security, SQLAlchemyUserDatastore, \

    UserMixin, RoleMixin, login_required

from flask_mail import Mail

 

# Create app

app = Flask(__name__)

app.config['DEBUG'] = True

app.config['SECRET_KEY'] = 'super-secret'

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'

 

app.config['SECURITY_REGISTERABLE'] = True

app.config['SECURITY_CONFIRMABLE'] = True

app.config['SECURITY_RECOVERABLE'] = True

 

 

# Por defecto sobre gmail

# Se puede utilizar cualquier cuenta de gmail u otra configuración

app.config['MAIL_SERVER'] = os.environ.get('SEC_MAIL_SERVER', 'smtp.gmail.com')

app.config['MAIL_PORT'] = os.environ.get('SEC_MAIL_PORT', 465)

app.config['MAIL_USE_SSL'] = os.environ.get('SEC_MAIL_SSL', True)

app.config['MAIL_USERNAME'] = os.environ.get('SEC_MAIL_USER')

app.config['MAIL_PASSWORD'] = os.environ.get('SEC_MAIL_PASS')

 

mail = Mail(app)

 

 

# Create database connection object

db = SQLAlchemy(app)

 

# Define models

roles_users = db.Table('roles_users',

        db.Column('user_id', db.Integer(), db.ForeignKey('user.id')),

        db.Column('role_id', db.Integer(), db.ForeignKey('role.id')))

 

class Role(db.Model, RoleMixin):

    id = db.Column(db.Integer(), primary_key=True)

    name = db.Column(db.String(80), unique=True)

    description = db.Column(db.String(255))

 

class User(db.Model, UserMixin):

    id = db.Column(db.Integer, primary_key=True)

    email = db.Column(db.String(255), unique=True)

    password = db.Column(db.String(255))

    active = db.Column(db.Boolean())

    confirmed_at = db.Column(db.DateTime())

    roles = db.relationship('Role', secondary=roles_users,

                            backref=db.backref('users', lazy='dynamic'))

 

# Setup Flask-Security

user_datastore = SQLAlchemyUserDatastore(db, User, Role)

security = Security(app, user_datastore)

 

# Create a user to test with

@app.before_first_request

def create_user():

    db.create_all()

    user_datastore.create_user(email='ejemplo@mail.com', password='aaaaaa',

                     confirmed_at=datetime.datetime.now())

    db.session.commit()

 

# Views

@app.route('/')

@login_required

def home():

    return render_template('index.html')

 

if __name__ == '__main__':

    app.run()

 

Hemos partido del ejemplo implementado con SQLAlchemy por ser el más rápido a la hora de desplegar ya que utiliza SQLite, una base de datos embebida que no necesita instalación.

En el código, por un lado decimos a la extensión que permita el registro de usuarios, la confirmación vía email de los registros y la recuperación de la contraseña en caso de olvido. Por otro, configuramos el servidor de correo para que nos permita el envío de los correos de registro, confirmación y recuperación. La configuración del correo se hace mediante variables de entorno. Esto permite emplear diferentes valores según dónde ejecutemos la aplicación (desarrollo, stage, producción).

Por otro lado hemos creado una página index para comprobar que el usuario se conecta correctamente:

¡Enhorabuena! Estás conectado como

<b>

{{current_user.email}}

</b>

<p>

<a href="{{ url_for_security('logout') }}">Salir</a>

De momento vemos que las páginas son feas, poco explícitas y además sólo están en inglés. En próximos posts veremos cómo cambiar el aspecto de las páginas, traducir los mensajes y alguna característica más de esta potente extensión. De momento, a modo de introducción, nos quedamos con este sencillísimo ejemplo.

Ya está disponible el código fuente completo está en github.

soporte aspa cloud

Déjanos ayudarte a elegir el producto que mejor se adapta a tus necesidades. Indícanos tu teléfono o email y nos ponemos en contacto contigo.

soporte aspa cloud

Déjanos ayudarte a elegir el producto que mejor se adapta a tus necesidades. Indícanos tu teléfono o email y nos ponemos en contacto contigo.