VeuReu commited on
Commit
e1e0418
·
verified ·
1 Parent(s): dea50db

Upload auth.py

Browse files
Files changed (1) hide show
  1. auth.py +318 -292
auth.py CHANGED
@@ -1,292 +1,318 @@
1
- """
2
- Módulo de autenticación para la aplicación Veureu.
3
- Gestiona usuarios, verificación de contraseñas y sincronización de usuarios por defecto.
4
- """
5
- import sys
6
- from datetime import datetime
7
- from pathlib import Path
8
-
9
- import streamlit as st
10
-
11
- from databases import get_user, create_user, update_user_password, get_all_users, log_action
12
- from mobile_verification import (
13
- initialize_sms_state,
14
- render_mobile_verification_screen,
15
- get_user_permissions,
16
- show_verification_status_in_sidebar
17
- )
18
- from persistent_data_gate import confirm_changes_and_logout
19
- from compliance_client import compliance_client
20
- import yaml
21
-
22
-
23
- def log(msg: str):
24
- """Helper per logging amb timestamp"""
25
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
26
- sys.stderr.write(f"[{timestamp}] {msg}\n")
27
- sys.stderr.flush()
28
-
29
-
30
- def verify_password(password: str, stored_password: str) -> bool:
31
- """Verifica la contraseña como texto plano."""
32
- return password == stored_password
33
-
34
-
35
- def create_default_users_if_needed():
36
- """Asegura que existan los usuarios por defecto y sus contraseñas esperadas (texto plano)."""
37
- log("Sincronizando usuarios per defecte (sense detalls sensibles)...")
38
- users_to_create = [
39
- ("verd", "verd123", "verd"),
40
- ("groc", "groc123", "groc"),
41
- ("taronja", "taronja123", "taronja"),
42
- ("blau", "blau123", "blau"),
43
- ("vermell", "vermell123", "vermell"),
44
- ]
45
- for username, password, role in users_to_create:
46
- try:
47
- row = get_user(username)
48
- if row:
49
- update_user_password(username, password)
50
- else:
51
- create_user(username, password, role)
52
- except Exception as e:
53
- log(f"Error sincronizando usuario {username}: {e}")
54
- log("Sincronització d'usuaris per defecte completada.")
55
-
56
-
57
- def initialize_auth_system(db_path: str):
58
- """Inicializa el sistema de autenticación y sincroniza usuarios."""
59
- if 'users_synced' not in st.session_state:
60
- create_default_users_if_needed()
61
- st.session_state['users_synced'] = True
62
-
63
- # Inicializar estado de verificación SMS
64
- initialize_sms_state()
65
-
66
- # Diagnòstic de base de dades simplificat (sense dades sensibles)
67
- if 'diag_logged' not in st.session_state:
68
- log("Base de dades d'usuaris inicialitzada correctament.")
69
- st.session_state['diag_logged'] = True
70
-
71
-
72
- def require_login(login_form_func):
73
- """Requiere que el usuario esté autenticado."""
74
- if not st.session_state.user:
75
- st.info("Por favor, inicia sesión para continuar.")
76
- login_form_func()
77
- st.stop()
78
-
79
-
80
- def render_login_form():
81
- """Renderiza el formulario de login con logs de depuración."""
82
- st.subheader("Inici de sessió")
83
- username = st.text_input("Usuari")
84
- password = st.text_input("Contrasenya", type="password")
85
-
86
- if st.button("Entrar", type="primary"):
87
- row = get_user(username)
88
-
89
- # Logs de depuración
90
- log("\n--- INTENTO DE LOGIN ---")
91
- log(f"Usuario introducido: '{username}'")
92
- log(f"Contraseña introducida: {'Sí' if password else 'No'}")
93
-
94
- if row:
95
- log(f"Usuario encontrado en BD: '{row['username']}'")
96
- stored_pw = (row["password_hash"] or "")
97
- log(f"Password almacenado (longitud): {len(stored_pw)}")
98
- is_valid = verify_password(password, stored_pw)
99
- log(f"Resultado de verify_password: {is_valid}")
100
- else:
101
- log("Usuario no encontrado en la BD.")
102
- is_valid = False
103
-
104
- log("--- FIN INTENTO DE LOGIN ---\n")
105
-
106
- if is_valid:
107
- st.session_state.user = {
108
- "id": row["id"],
109
- "username": row["username"],
110
- "role": row["role"]
111
- }
112
- # Desa la darrera contrasenya per poder-la registrar als esdeveniments
113
- st.session_state.last_password = password
114
-
115
- # Registre d'esdeveniment de login a events.db
116
- try:
117
- session_id = st.session_state.get("session_id", "")
118
- phone = (
119
- st.session_state.get("sms_phone_verified")
120
- or st.session_state.get("sms_phone")
121
- or ""
122
- )
123
- log_action(
124
- session=session_id,
125
- user=username or "",
126
- phone=phone,
127
- action="login",
128
- sha1sum="",
129
- )
130
- except Exception as e:
131
- log(f"Error registrant esdeveniment de login: {e}")
132
-
133
- st.success(f"Benvingut/da, {row['username']}")
134
- st.rerun()
135
- else:
136
- st.error("Credencials invàlides")
137
-
138
-
139
- def render_sidebar():
140
- """Renderiza la barra lateral con información de usuario y navegación."""
141
- role = st.session_state.user["role"] if st.session_state.user else None
142
-
143
- with st.sidebar:
144
- logo_path = Path(__file__).parent / "images" / "veureu.png"
145
- if logo_path.exists():
146
- st.image(str(logo_path), width=140)
147
- else:
148
- st.title("Veureu")
149
- if st.session_state.user:
150
- st.write(f"Usuari: **{st.session_state.user['username']}** (rol: {st.session_state.user['role']})")
151
-
152
- # Mostrar estado de verificación SMS
153
- show_verification_status_in_sidebar()
154
-
155
- if st.session_state.user:
156
- # Obtener permisos del usuario
157
- permissions = get_user_permissions(
158
- role,
159
- st.session_state.get('sms_verified')
160
- )
161
-
162
- # Construir opciones de navegación según permisos
163
- page_options = []
164
-
165
- if permissions["analizar"]:
166
- page_options.append("Analitzar audiodescripcions")
167
-
168
- if permissions["procesar_videos"]:
169
- page_options.append("Processar vídeo nou")
170
-
171
- if permissions["estadisticas"]:
172
- page_options.append("Estadístiques")
173
-
174
- if permissions["validar"]:
175
- page_options.append("Validació")
176
-
177
- # Opció de configuració avançada (sala de màquines), només per a l'usuari 'verd'
178
- if st.session_state.user.get("username") == "verd":
179
- page_options.append("Sala de màquines")
180
-
181
- # Si no hay opciones disponibles, mostrar solo análisis
182
- if not page_options:
183
- page_options = ["Analitzar audiodescripcions"]
184
-
185
- page = st.radio(
186
- "Navegació",
187
- page_options,
188
- index=0
189
- )
190
- st.markdown("---")
191
-
192
- if st.button(
193
- "Confirmar canvis i tancar sessió",
194
- key="confirmar_canvis_tancar",
195
- use_container_width=True,
196
- type="primary",
197
- ):
198
- # Persistir canvis de la sessió actual abans de tancar
199
- try:
200
- base_dir = Path(__file__).parent
201
- session_id = st.session_state.get("session_id", "")
202
- api_client = st.session_state.get("api_client")
203
- digest_info = confirm_changes_and_logout(base_dir, api_client, session_id)
204
- except Exception:
205
- digest_info = None
206
-
207
- # Llegir flag public_blockchain_enabled de config.yaml
208
- try:
209
- cfg_path = Path(__file__).parent / "config.yaml"
210
- with cfg_path.open("r", encoding="utf-8") as f:
211
- cfg = yaml.safe_load(f) or {}
212
- comp_cfg = cfg.get("compliance", {}) or {}
213
- public_blockchain_enabled = bool(
214
- comp_cfg.get(
215
- "public_blockchain_enabled",
216
- comp_cfg.get("public_blockchain_enable", False),
217
- )
218
- )
219
- except Exception:
220
- public_blockchain_enabled = False
221
-
222
- blockchain_published = False
223
- polygonscan_url = None
224
-
225
- if public_blockchain_enabled and digest_info and digest_info.get("events_digest"):
226
- try:
227
- session_id = st.session_state.get("session_id", "")
228
- resp = compliance_client.publish_events_digest(
229
- session_id=session_id,
230
- digest_hash=digest_info["events_digest"],
231
- )
232
- if resp:
233
- polygonscan_url = resp.get("transaction_url")
234
- blockchain_published = bool(resp.get("transaction_hash"))
235
- except Exception:
236
- blockchain_published = False
237
-
238
- # Registrar desconnexió a events.db
239
- try:
240
- current_user = st.session_state.user or {}
241
- session_id = st.session_state.get("session_id", "")
242
- phone = (
243
- st.session_state.get("sms_phone_verified")
244
- or st.session_state.get("sms_phone")
245
- or ""
246
- )
247
- last_password = st.session_state.get("last_password", "")
248
- log_action(
249
- session=session_id,
250
- user=current_user.get("username", ""),
251
- phone=phone,
252
- action="logout",
253
- sha1sum="",
254
- )
255
- except Exception as e:
256
- log(f"Error registrant esdeveniment de logout: {e}")
257
-
258
- # Anotar al log el resultat de la publicació a Polygon (si aplica)
259
- digest_hash = digest_info.get("events_digest") if digest_info else None
260
- events_count = digest_info.get("events_count") if digest_info else None
261
- log(
262
- "Logout completat: "
263
- f"session={session_id or '-'} "
264
- f"events_digest={digest_hash or '-'} "
265
- f"events_count={events_count if events_count is not None else '-'} "
266
- f"polygon_published={'sí' if blockchain_published else 'no'} "
267
- f"polygon_url={polygonscan_url or '-'}"
268
- )
269
-
270
- # Netejar sessió d'usuari
271
- st.session_state.user = None
272
- st.session_state.sms_verified = None
273
-
274
- # Mostrar missatge segons estat de publicació
275
- if public_blockchain_enabled and blockchain_published and polygonscan_url:
276
- st.success(
277
- "✅ Els canvis s'han desat i s'han publicat a la cadena de blocs de Polygon. "
278
- f"Pots consultar la transacció a: {polygonscan_url}"
279
- )
280
- elif public_blockchain_enabled:
281
- st.info(
282
- " Els canvis s'han desat, però no s'ha pogut completar la publicació "
283
- "a la cadena de blocs de Polygon en aquest moment."
284
- )
285
- else:
286
- st.info(" Canvis desats (sense publicació a blockchain).")
287
-
288
- st.rerun()
289
- else:
290
- page = None
291
-
292
- return page, role
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Módulo de autenticación para la aplicación Veureu.
3
+ Gestiona usuarios, verificación de contraseñas y sincronización de usuarios por defecto.
4
+ """
5
+ import sys
6
+ from datetime import datetime
7
+ from pathlib import Path
8
+
9
+ import streamlit as st
10
+
11
+ from databases import get_user, create_user, update_user_password, get_all_users, log_action
12
+ from mobile_verification import (
13
+ initialize_sms_state,
14
+ render_mobile_verification_screen,
15
+ get_user_permissions,
16
+ show_verification_status_in_sidebar
17
+ )
18
+ from persistent_data_gate import confirm_changes_and_logout
19
+ from compliance_client import compliance_client
20
+ import yaml
21
+
22
+
23
+ def log(msg: str):
24
+ """Helper per logging amb timestamp"""
25
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
26
+ sys.stderr.write(f"[{timestamp}] {msg}\n")
27
+ sys.stderr.flush()
28
+
29
+
30
+ def verify_password(password: str, stored_password: str) -> bool:
31
+ """Verifica la contraseña como texto plano."""
32
+ return password == stored_password
33
+
34
+
35
+ def create_default_users_if_needed():
36
+ """Asegura que existan los usuarios por defecto y sus contraseñas esperadas (texto plano)."""
37
+ log("Sincronizando usuarios per defecte (sense detalls sensibles)...")
38
+ users_to_create = [
39
+ ("verd", "verd123", "verd"),
40
+ ("groc", "groc123", "groc"),
41
+ ("taronja", "taronja123", "taronja"),
42
+ ("blau", "blau123", "blau"),
43
+ ("vermell", "vermell123", "vermell"),
44
+ ]
45
+ for username, password, role in users_to_create:
46
+ try:
47
+ row = get_user(username)
48
+ if row:
49
+ update_user_password(username, password)
50
+ else:
51
+ create_user(username, password, role)
52
+ except Exception as e:
53
+ log(f"Error sincronizando usuario {username}: {e}")
54
+ log("Sincronització d'usuaris per defecte completada.")
55
+
56
+
57
+ def initialize_auth_system(db_path: str):
58
+ """Inicializa el sistema de autenticación y sincroniza usuarios."""
59
+ if 'users_synced' not in st.session_state:
60
+ create_default_users_if_needed()
61
+ st.session_state['users_synced'] = True
62
+
63
+ # Inicializar estado de verificación SMS
64
+ initialize_sms_state()
65
+
66
+ # Diagnòstic de base de dades simplificat (sense dades sensibles)
67
+ if 'diag_logged' not in st.session_state:
68
+ log("Base de dades d'usuaris inicialitzada correctament.")
69
+ st.session_state['diag_logged'] = True
70
+
71
+
72
+ def require_login(login_form_func):
73
+ """Requiere que el usuario esté autenticado."""
74
+ if not st.session_state.user:
75
+ st.info("Por favor, inicia sesión para continuar.")
76
+ login_form_func()
77
+ st.stop()
78
+
79
+
80
+ def render_login_form():
81
+ """Renderiza el formulario de login con logs de depuración."""
82
+ st.subheader("Inici de sessió")
83
+ username = st.text_input("Usuari")
84
+ password = st.text_input("Contrasenya", type="password")
85
+
86
+ if st.button("Entrar", type="primary"):
87
+ row = get_user(username) if username else None
88
+
89
+ # Logs de depuración
90
+ log("\n--- INTENTO DE LOGIN ---")
91
+ log(f"Usuario introducido: '{username}'")
92
+ log(f"Contraseña introducida: {'Sí' if password else 'No'}")
93
+
94
+ if row:
95
+ log(f"Usuario encontrado en BD: '{row['username']}'")
96
+ stored_pw = (row["password_hash"] or "")
97
+ log(f"Password almacenado (longitud): {len(stored_pw)}")
98
+ is_valid = verify_password(password, stored_pw)
99
+ log(f"Resultado de verify_password: {is_valid}")
100
+ else:
101
+ log("Usuario no encontrado en la BD o nombre de usuario vacío. Se asignará rol 'vermell'.")
102
+ is_valid = False
103
+
104
+ log("--- FIN INTENTO DE LOGIN ---\n")
105
+
106
+ if is_valid:
107
+ st.session_state.user = {
108
+ "id": row["id"],
109
+ "username": row["username"],
110
+ "role": row["role"]
111
+ }
112
+ # Desa la darrera contrasenya per poder-la registrar als esdeveniments
113
+ st.session_state.last_password = password
114
+
115
+ # Registre d'esdeveniment de login a events.db
116
+ try:
117
+ session_id = st.session_state.get("session_id", "")
118
+ phone = (
119
+ st.session_state.get("sms_phone_verified")
120
+ or st.session_state.get("sms_phone")
121
+ or ""
122
+ )
123
+ log_action(
124
+ session=session_id,
125
+ user=username or "",
126
+ phone=phone,
127
+ action="login",
128
+ sha1sum="",
129
+ )
130
+ except Exception as e:
131
+ log(f"Error registrant esdeveniment de login: {e}")
132
+
133
+ st.success(f"Benvingut/da, {row['username']}")
134
+ st.rerun()
135
+ else:
136
+ # Si l'usuari està buit o no existeix, entrar com a "vermell" (visitant)
137
+ st.session_state.user = {
138
+ "id": None,
139
+ "username": "vermell",
140
+ "role": "vermell",
141
+ }
142
+ st.session_state.last_password = password
143
+
144
+ try:
145
+ session_id = st.session_state.get("session_id", "")
146
+ phone = (
147
+ st.session_state.get("sms_phone_verified")
148
+ or st.session_state.get("sms_phone")
149
+ or ""
150
+ )
151
+ log_action(
152
+ session=session_id,
153
+ user="vermell",
154
+ phone=phone,
155
+ action="login",
156
+ sha1sum="",
157
+ )
158
+ except Exception as e:
159
+ log(f"Error registrant esdeveniment de login per usuari 'vermell': {e}")
160
+
161
+ st.success("Benvingut/da, vermell")
162
+ st.rerun()
163
+
164
+
165
+ def render_sidebar():
166
+ """Renderiza la barra lateral con información de usuario y navegación."""
167
+ role = st.session_state.user["role"] if st.session_state.user else None
168
+
169
+ with st.sidebar:
170
+ logo_path = Path(__file__).parent / "images" / "veureu.png"
171
+ if logo_path.exists():
172
+ st.image(str(logo_path), width=140)
173
+ else:
174
+ st.title("Veureu")
175
+ if st.session_state.user:
176
+ st.write(f"Usuari: **{st.session_state.user['username']}** (rol: {st.session_state.user['role']})")
177
+
178
+ # Mostrar estado de verificación SMS
179
+ show_verification_status_in_sidebar()
180
+
181
+ if st.session_state.user:
182
+ # Obtener permisos del usuario
183
+ permissions = get_user_permissions(
184
+ role,
185
+ st.session_state.get('sms_verified')
186
+ )
187
+
188
+ # Construir opciones de navegación según permisos
189
+ page_options = []
190
+
191
+ if permissions["analizar"]:
192
+ page_options.append("Analitzar audiodescripcions")
193
+
194
+ if permissions["procesar_videos"]:
195
+ page_options.append("Processar vídeo nou")
196
+
197
+ if permissions["estadisticas"]:
198
+ page_options.append("Estadístiques")
199
+
200
+ if permissions["validar"]:
201
+ page_options.append("Validació")
202
+
203
+ # Opció de configuració avançada (sala de màquines), només per a l'usuari 'verd'
204
+ if st.session_state.user.get("username") == "verd":
205
+ page_options.append("Sala de màquines")
206
+
207
+ # Si no hay opciones disponibles, mostrar solo análisis
208
+ if not page_options:
209
+ page_options = ["Analitzar audiodescripcions"]
210
+
211
+ page = st.radio(
212
+ "Navegació",
213
+ page_options,
214
+ index=0
215
+ )
216
+ st.markdown("---")
217
+
218
+ if st.button(
219
+ "Confirmar canvis i tancar sessió",
220
+ key="confirmar_canvis_tancar",
221
+ use_container_width=True,
222
+ type="primary",
223
+ ):
224
+ # Persistir canvis de la sessió actual abans de tancar
225
+ try:
226
+ base_dir = Path(__file__).parent
227
+ session_id = st.session_state.get("session_id", "")
228
+ api_client = st.session_state.get("api_client")
229
+ digest_info = confirm_changes_and_logout(base_dir, api_client, session_id)
230
+ except Exception:
231
+ digest_info = None
232
+
233
+ # Llegir flag public_blockchain_enabled de config.yaml
234
+ try:
235
+ cfg_path = Path(__file__).parent / "config.yaml"
236
+ with cfg_path.open("r", encoding="utf-8") as f:
237
+ cfg = yaml.safe_load(f) or {}
238
+ comp_cfg = cfg.get("compliance", {}) or {}
239
+ public_blockchain_enabled = bool(
240
+ comp_cfg.get(
241
+ "public_blockchain_enabled",
242
+ comp_cfg.get("public_blockchain_enable", False),
243
+ )
244
+ )
245
+ except Exception:
246
+ public_blockchain_enabled = False
247
+
248
+ blockchain_published = False
249
+ polygonscan_url = None
250
+
251
+ if public_blockchain_enabled and digest_info and digest_info.get("events_digest"):
252
+ try:
253
+ session_id = st.session_state.get("session_id", "")
254
+ resp = compliance_client.publish_events_digest(
255
+ session_id=session_id,
256
+ digest_hash=digest_info["events_digest"],
257
+ )
258
+ if resp:
259
+ polygonscan_url = resp.get("transaction_url")
260
+ blockchain_published = bool(resp.get("transaction_hash"))
261
+ except Exception:
262
+ blockchain_published = False
263
+
264
+ # Registrar desconnexió a events.db
265
+ try:
266
+ current_user = st.session_state.user or {}
267
+ session_id = st.session_state.get("session_id", "")
268
+ phone = (
269
+ st.session_state.get("sms_phone_verified")
270
+ or st.session_state.get("sms_phone")
271
+ or ""
272
+ )
273
+ last_password = st.session_state.get("last_password", "")
274
+ log_action(
275
+ session=session_id,
276
+ user=current_user.get("username", ""),
277
+ phone=phone,
278
+ action="logout",
279
+ sha1sum="",
280
+ )
281
+ except Exception as e:
282
+ log(f"Error registrant esdeveniment de logout: {e}")
283
+
284
+ # Anotar al log el resultat de la publicació a Polygon (si aplica)
285
+ digest_hash = digest_info.get("events_digest") if digest_info else None
286
+ events_count = digest_info.get("events_count") if digest_info else None
287
+ log(
288
+ "Logout completat: "
289
+ f"session={session_id or '-'} "
290
+ f"events_digest={digest_hash or '-'} "
291
+ f"events_count={events_count if events_count is not None else '-'} "
292
+ f"polygon_published={'sí' if blockchain_published else 'no'} "
293
+ f"polygon_url={polygonscan_url or '-'}"
294
+ )
295
+
296
+ # Netejar sessió d'usuari
297
+ st.session_state.user = None
298
+ st.session_state.sms_verified = None
299
+
300
+ # Mostrar missatge segons estat de publicació
301
+ if public_blockchain_enabled and blockchain_published and polygonscan_url:
302
+ st.success(
303
+ "✅ Els canvis s'han desat i s'han publicat a la cadena de blocs de Polygon. "
304
+ f"Pots consultar la transacció a: {polygonscan_url}"
305
+ )
306
+ elif public_blockchain_enabled:
307
+ st.info(
308
+ "✅ Els canvis s'han desat, però no s'ha pogut completar la publicació "
309
+ "a la cadena de blocs de Polygon en aquest moment."
310
+ )
311
+ else:
312
+ st.info("✅ Canvis desats (sense publicació a blockchain).")
313
+
314
+ st.rerun()
315
+ else:
316
+ page = None
317
+
318
+ return page, role