File size: 7,017 Bytes
82bf6fc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# 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",
]