jueves, 12 de septiembre de 2019

Módulo de Login y Registro en Python - Gestión de Datos

Este artículo pertenece al proyecto Login y Registro de usuarios en python y es la continuación del capítulo de Template y menú del sistema. En esta ocasión introduciremos al sistema la gestión de los datos ingresado a través del menú creado anteriormente. El código lo puede encontrar acá.

Antes de iniciar debemos activar el entorno virtual e instalar bcrypt paquete que se usará para la encryptación del password. Para instalar bcrypt ejecutamos:

# pip install bcrypt

ACTUALIZACIÓN DEL MAIN

En esta ocasión solo actualizaremos la carpeta main.py, quedando de la siguiente forma:

# -*- coding: utf-8 -*-
import time
import bcrypt
from menu import MenuTemplate

username = []
email = []
password = []

def main():
    menu = MenuTemplate()
    opcion = menu.main_menu()

    if opcion == "1":
        ''' Sección para el registro de usuario '''
        data = menu.register()

        if data["username"] in username:
            print(f'Este username: <{data["username"]}> ya se encuentra registrado.')
        elif data["email"] in email:
            print(f'Este Email: <{data["email"]}> ya se encuentra registrado.')
        elif not data["password1"] == data["password2"]:
            print(f'Las contraseñas no coinciden.')
        else:
            password_hashed = bcrypt.hashpw(
                data["password1"].encode('utf8'), 
                bcrypt.gensalt()
            )
            
            username.append(data["username"])
            email.append(data["email"])
            password.append(password_hashed)

            print ("Se ha registrado en el sistema.")

        time.sleep(2)
        main()

    elif opcion == "2":
        ''' Sección para el login de usuario '''
        data = menu.login()
        index = 0
        try:
            index = username.index(data['username'])
            
            password_hashed = password[index]
            if not bcrypt.checkpw(data["password"].encode('utf8'), password_hashed):
                print("Username o password inválido.")
            else:
                print("Se ha autenticado de forma correcta.")

        except ValueError as e:
            print("Username o password inválido.")

        time.sleep(2)
        main()

    elif opcion == "3":
        ''' Sección para salir del sistema'''
        menu.exit_text()
        time.sleep(2)
        menu.clean_screen()

    else:
        ''' En caso de no coincidir la opciones anteriores '''
        print("\n Opción incorrecta. Intente de nuevo")
        time.sleep(2)
        main()

main()

Iniciemos estudiando los nuevos cambios ingresados al código por parte:

import time
import bcrypt
from menu import MenuTemplate

username = []
email = []
password = []

El primer segmento de código modificado se encuentra justamente al inicio. En ella se agregó:
  • La importación del módulo bcrypt (import bcrypt); éste módulo será requerido para encryptar la contraseña que el usuario ingresa al registrarse.
  • Luego creamos tres listas vacías: username, email, password, para almacenar los datos ingresados por el usuario.
La otra modificación se realizó cuando la opción es 1; es decir, cuando el usuario elige registrarse.

if opcion == "1":
        ''' Sección para el registro de usuario '''
        data = menu.register()

        if data["username"] in username:
            print(f'Este username: <{data["username"]}> ya se encuentra registrado.')
        elif data["email"] in email:
            print(f'Este Email: <{data["email"]}> ya se encuentra registrado.')
        elif not data["password1"] == data["password2"]:
            print(f'Las contraseñas no coinciden.')
        else:
            password_hashed = bcrypt.hashpw(
                data["password1"].encode('utf8'), 
                bcrypt.gensalt()
            )
            
            username.append(data["username"])
            email.append(data["email"])
            password.append(password_hashed)

            print ("Se ha registrado en el sistema.")

        time.sleep(2)
        main()

Nótese que la variable data recibe la información del registro en forma de diccionario. Luego:
  • Se verifica si el nombre de usuario (username) ingresado por el usuario se encuentra en la lista username (if data["username"] in username). De encontrarse se muestra un mensaje indicando que ese username ya se encuentra registrado.
  • También se verifica que el email ingresado por el usuario no se encuentre registrado (elif data["email"] in email). Si se encuentra registrado muestra un mensaje indicándolo.
  • Mas adelante se valida que el password1 y password2 coincida (elif not data["password1"] == data["password2"]). Si no coinciden se muestra un mensaje de error. 
  • Todas estas validaciones básicas permiten filtrar la unicidad del username e email, evitando que se registren dos usuarios con el mismo username e email. Asimismo que cuando se ingrese el password (las dos veces) sean coincidente.
  • Si no se cumple se procede a encryptar el password guardándolo en una variable llamada password_hashed, para posteriormente agregar los nuevos datos a las listas. Esto se hace con la función append, la cual permite agregar un nuevo elemento a las estructuras tipo lista.
  • Nótese que al mostrar los mensajes de error se concatena el texto con el diccionario dato colocando como prefijo f, siendo éste una forma elegante de combinar texto y variable. También se pudo haber hecho:
    • print("Este username: <"+data["username"]+"> ya se encuentra registrado").
    • print("Este username: <{}> ya se encuentra registrado".format(data["username"])).
Otra sección que sufrió modificación es donde se verifica la opcion 2, que corresponde al login de usuario.

elif opcion == "2":
        ''' Sección para el login de usuario '''
        data = menu.login()
        index = 0
        try:
            index = username.index(data['username'])
            
            password_hashed = password[index]
            if not bcrypt.checkpw(data["password"].encode('utf8'), password_hashed):
                print("Username o password inválido.")
            else:
                print("Se ha autenticado de forma correcta.")

        except ValueError as e:
            print("Username o password inválido.")

        time.sleep(2)
        main()

De igual forma se recibe la información del login en forma de diccionario en la variable data. Después se:
  •  Crea una variable index cuyo valor por defecto es 0 (index = 0). Esta variable se utilizará para buscar el password en la posición donde se almacenó el username.
  • Se construye un bloque try - except esto debido a que dentro del try se ejecuta la instrucción username.index(data["username"]) la que permite obtener el index donde se encuentra el nombre de usuario ingresado por teclado. Si no se encuentra se dispara una excepción ValueError la cual capturamos en except. En otras palabras, se buscan dentro de la lista username el nombre se usuario teclado, si se encuentra se devuelve el index donde se encontró, sino se dispara la excepción. En la excepción mostramos un mensaje indicando que el Username o Password es inválido.
  • Si el nombre de usuario se encuentra dentro de la lista username se obtiene el index el cual es utilizado para extraer de la lista password el password encryptado (password_hashed = passsword[index]).
  • Usando una función de bcrypt.checkpw se compara el password ingresado con el encryptado. Si no hay coincidencia se muestra un mensaje indicando Username o Password inválido. De lo contrario se muestra un mensaje indicado que se ha logeado correctamente.
Nótese que aunque en el login sea erróneo el username o el password se muestra el mismo mensaje "Username o password inválido", esto se hace por cuestiones de seguridad. Ya que si mostramos el error por separado le estamos indicios a un posible atacante sobre cual de los dos datos ingresado es el erróneo.

Con esto finalizamos esta capítulo. En el próximo añadiremos validaciones más específicas al dato del email y password.


0 comentarios:

Publicar un comentario