-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathauth.py
More file actions
211 lines (162 loc) · 5.76 KB
/
Copy pathauth.py
File metadata and controls
211 lines (162 loc) · 5.76 KB
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
import bcrypt
import psycopg2
import streamlit as st
from database import get_conn, execute, read_df
from utils import now_utc
import secrets
def hash_password(password):
password_bytes = password.encode("utf-8")
salt = bcrypt.gensalt()
hashed = bcrypt.hashpw(password_bytes, salt)
return hashed.decode("utf-8")
def verify_password(password, password_hash):
password_bytes = password.encode("utf-8")
hash_bytes = password_hash.encode("utf-8")
return bcrypt.checkpw(password_bytes, hash_bytes)
def register_user(name, username, password):
name = name.strip()
username = username.strip().lower()
password = password.strip()
if not name or not username or not password:
return False, "Todos los campos son obligatorios."
if len(password) < 6:
return False, "La contraseña debe tener al menos 6 caracteres."
password_hash = hash_password(password)
try:
execute(
"""
INSERT INTO users(name, username, password_hash, created_at)
VALUES (%s, %s, %s, %s)
""",
(name, username, password_hash, now_utc().isoformat()),
)
return True, "Usuario registrado correctamente. Ahora puedes iniciar sesión."
except psycopg2.IntegrityError:
return False, "Ese nombre de usuario ya existe. Elige otro."
def login_user(username, password):
username = username.strip().lower()
password = password.strip()
if not username or not password:
return False, "Ingresa usuario y contraseña."
conn = get_conn()
cur = conn.cursor()
cur.execute(
"""
SELECT id, name, username, password_hash
FROM users
WHERE username = %s
""",
(username,),
)
user = cur.fetchone()
conn.close()
if not user:
return False, "Usuario o contraseña incorrectos."
user_id, name, username, password_hash = user
if not verify_password(password, password_hash):
return False, "Usuario o contraseña incorrectos."
st.session_state["user_id"] = user_id
st.session_state["name"] = name
st.session_state["username"] = username
return True, f"Bienvenido, {name}."
def sidebar_auth():
st.sidebar.header("Cuenta")
if "user_id" in st.session_state:
st.sidebar.success(f"Sesión iniciada como: {st.session_state['name']}")
if st.sidebar.button("Cerrar sesión"):
st.session_state.pop("user_id", None)
st.session_state.pop("name", None)
st.session_state.pop("username", None)
st.rerun()
return st.session_state["user_id"], st.session_state["name"]
auth_mode = st.sidebar.radio(
"Acceso",
["Iniciar sesión", "Crear cuenta"]
)
if auth_mode == "Iniciar sesión":
username = st.sidebar.text_input("Usuario")
password = st.sidebar.text_input("Contraseña", type="password")
if st.sidebar.button("Entrar"):
ok, msg = login_user(username, password)
if ok:
st.sidebar.success(msg)
st.rerun()
else:
st.sidebar.error(msg)
else:
name = st.sidebar.text_input("Nombre visible", placeholder="Ej. Lolazo777")
username = st.sidebar.text_input("Usuario", placeholder="Ej. Lolazo777")
password = st.sidebar.text_input("Contraseña", type="password")
confirm_password = st.sidebar.text_input("Confirmar contraseña", type="password")
# if st.sidebar.button("Crear cuenta"):
# if password != confirm_password:
# st.sidebar.error("Las contraseñas no coinciden.")
# else:
# ok, msg = register_user(name, username, password)
# if ok:
# st.sidebar.success(msg)
# else:
# st.sidebar.error(msg)
return None, None
def change_password(user_id, current_password, new_password, confirm_password):
current_password = current_password.strip()
new_password = new_password.strip()
confirm_password = confirm_password.strip()
if not current_password or not new_password or not confirm_password:
return False, "Todos los campos son obligatorios."
if new_password != confirm_password:
return False, "Las nuevas contraseñas no coinciden."
if len(new_password) < 6:
return False, "La nueva contraseña debe tener al menos 6 caracteres."
conn = get_conn()
cur = conn.cursor()
cur.execute(
"""
SELECT password_hash
FROM users
WHERE id = %s
""",
(user_id,),
)
row = cur.fetchone()
conn.close()
if not row:
return False, "Usuario no encontrado."
password_hash = row[0]
if not verify_password(current_password, password_hash):
return False, "La contraseña actual es incorrecta."
new_hash = hash_password(new_password)
execute(
"""
UPDATE users
SET password_hash = %s
WHERE id = %s
""",
(new_hash, user_id),
)
return True, "Contraseña actualizada correctamente."
def admin_reset_password(username):
username = username.strip().lower()
if not username:
return False, "Debes seleccionar un usuario.", None
temp_password = secrets.token_hex(4)
new_hash = hash_password(temp_password)
result = read_df(
"""
SELECT id
FROM users
WHERE username = %s
""",
(username,),
)
if result.empty:
return False, "Usuario no encontrado.", None
execute(
"""
UPDATE users
SET password_hash = %s
WHERE username = %s
""",
(new_hash, username),
)
return True, "Contraseña temporal generada correctamente.", temp_password