# modules/auth/auth.py import gradio as gr import os import logging import bcrypt import base64 from azure.cosmos import CosmosClient from azure.cosmos.exceptions import CosmosHttpResponseError from datetime import datetime, timezone from ..database.sql_db import ( get_user, get_student_user, create_student_user, update_student_user, delete_student_user, record_login, record_logout, ) logger = logging.getLogger(__name__) # Verificar las variables de entorno necesarias COSMOS_ENDPOINT = os.getenv("COSMOS_ENDPOINT") COSMOS_KEY = os.getenv("COSMOS_KEY") if not COSMOS_ENDPOINT or not COSMOS_KEY: raise ValueError("Las variables de entorno COSMOS_ENDPOINT y COSMOS_KEY no están configuradas.") # Validar y limpiar la clave de Cosmos DB def clean_and_validate_key(key): key = key.strip() while len(key) % 4 != 0: key += "=" try: base64.b64decode(key) return key except Exception: raise ValueError("La clave proporcionada no es válida.") COSMOS_KEY = clean_and_validate_key(COSMOS_KEY) cosmos_client = CosmosClient(COSMOS_ENDPOINT, COSMOS_KEY) ######################################## # ÍNDICE DE FUNCIONES (Referencia consolidada) ######################################## # Autenticación # - authenticate_user # - authenticate_student # - authenticate_admin # Manejo de contraseñas # - hash_password # - verify_password # Manejo de usuarios # - register_student # - update_student_info # - delete_student # Interfaz de Gradio # - create_auth_interface # - create_user_page ######################################## # Funciones de Autenticación ######################################## def authenticate_user(username: str, password: str) -> tuple[bool, str | None]: """ Autentica un usuario utilizando la base de datos. """ try: user = get_user(username) if user and verify_password(user["password"], password): logger.info(f"Usuario autenticado: {username}, Rol: {user['role']}") return True, user["role"] logger.warning(f"Credenciales incorrectas para el usuario: {username}") return False, None except Exception as e: logger.error(f"Error autenticando al usuario {username}: {str(e)}") return False, None def authenticate_student(username, password): """Autentica a un estudiante.""" success, role = authenticate_user(username, password) return (success, role) if role == "Estudiante" else (False, None) def authenticate_admin(username, password): """Autentica a un administrador.""" success, role = authenticate_user(username, password) return (success, role) if role == "Administrador" else (False, None) ######################################## # Registro y Manejo de Usuarios ######################################## def register_student(username: str, password: str, additional_info=None) -> bool: """ Registra un nuevo estudiante. """ try: if get_student_user(username): logger.warning(f"El estudiante {username} ya existe.") return False hashed_password = hash_password(password) additional_info = additional_info or {} additional_info["role"] = "Estudiante" create_student_user(username, hashed_password, additional_info) logger.info(f"Estudiante registrado: {username}") return True except Exception as e: logger.error(f"Error registrando al estudiante {username}: {str(e)}") return False def update_student_info(username: str, new_info: dict) -> bool: """ Actualiza la información de un estudiante. """ try: if "password" in new_info: new_info["password"] = hash_password(new_info["password"]) return update_student_user(username, new_info) except Exception as e: logger.error(f"Error actualizando información del estudiante {username}: {str(e)}") return False def delete_student(username: str) -> bool: """ Elimina un estudiante de la base de datos. """ try: return delete_student_user(username) except Exception as e: logger.error(f"Error al eliminar estudiante {username}: {str(e)}") return False ######################################## # Manejo de Contraseñas ######################################## def hash_password(password: str) -> str: """Hashea una contraseña utilizando bcrypt.""" return bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()).decode("utf-8") def verify_password(stored_password: str, provided_password: str) -> bool: """Verifica que una contraseña coincida con su hash.""" return bcrypt.checkpw(provided_password.encode("utf-8"), stored_password.encode("utf-8")) ######################################## # Interfaz Gradio para Login ######################################## def create_auth_interface(): """ Crea la interfaz de autenticación. """ with gr.Blocks() as auth_interface: gr.Markdown("# Login") username = gr.Textbox(label="Usuario") password = gr.Textbox(label="Contraseña", type="password") login_btn = gr.Button("Iniciar Sesión") message = gr.Markdown() def handle_login(user, pwd): success, role = authenticate_user(user, pwd) return f"Bienvenido, {user} ({role})" if success else "Credenciales incorrectas." login_btn.click(fn=handle_login, inputs=[username, password], outputs=message) return auth_interface ######################################## # Página de Usuario ######################################## def create_user_page(): """ Crea una página de usuario simple tras el inicio de sesión. """ with gr.Blocks() as user_page: gr.Markdown("# Bienvenido a la Página de Usuario") gr.Markdown("Esta página está disponible después de un inicio de sesión exitoso.") username = gr.Textbox(label="Usuario", interactive=False) role = gr.Textbox(label="Rol", interactive=False) def load_user_info(): return "UsuarioPrueba", "Estudiante" # Simula datos de sesión user_page.load(fn=load_user_info, inputs=[], outputs=[username, role]) gr.Button("Cerrar Sesión").click( fn=lambda: "Sesión cerrada", inputs=[], outputs=[user_page] ) return user_page ######################################## # Exportar Funciones ######################################## __all__ = [ "create_auth_interface", "create_user_page", "register_student", "authenticate_user", "authenticate_student", "authenticate_admin", "update_student_info", "delete_student", "hash_password", "verify_password", ]