Roudrigus commited on
Commit
d79bb40
·
verified ·
1 Parent(s): d310f8c

Update login.py

Browse files
Files changed (1) hide show
  1. login.py +182 -235
login.py CHANGED
@@ -1,260 +1,207 @@
1
  # -*- coding: utf-8 -*-
 
 
 
 
 
 
 
 
 
 
 
 
2
  import os
 
3
  import streamlit as st
4
 
5
- from banco import SessionLocal
6
- from models import Usuario
7
- from utils_seguranca import verificar_senha
8
- from utils_auditoria import registrar_log
9
-
10
- # 🔀 Roteador de banco (Produção/Teste/Treinamento) — opcional
11
- # Se não houver db_router.py, usamos fallback suave.
12
  try:
13
- from db_router import (
14
- set_db_choice,
15
- current_db_choice,
16
- list_banks,
17
- bank_label,
18
- get_session_for_current_db, # opcional — se existir, usamos a sessão certa
19
- )
20
- _HAS_ROUTER = True
21
  except Exception:
22
- _HAS_ROUTER = False
23
 
24
- def set_db_choice(choice: str):
25
- st.session_state["__db_choice__"] = (choice or "prod").lower()
 
26
 
27
- def current_db_choice() -> str:
28
- return st.session_state.get("__db_choice__", "prod")
 
29
 
30
- def list_banks():
31
- return ["prod", "test"]
 
32
 
33
- def bank_label(choice: str) -> str:
34
- return {"prod": "Banco 1 (📗 Produção)", "test": "Banco 2 (📕 Teste)"}.get(choice, choice)
35
 
36
- # -----------------------------------------------------------------------------
37
- # Sessão de banco por ambiente
38
- # -----------------------------------------------------------------------------
39
- def _get_db_session():
40
- """
41
- Se o router tiver uma fábrica de sessão por banco, usa ela.
42
- Caso contrário, usa SessionLocal() padrão.
43
- """
44
  try:
45
- if _HAS_ROUTER and callable(get_session_for_current_db): # type: ignore[name-defined]
46
- return get_session_for_current_db() # db per ambiente
47
- except Exception:
48
- pass
49
- return SessionLocal()
50
-
51
- # -----------------------------------------------------------------------------
52
- # Efeito de aniversário (visual)
53
- # -----------------------------------------------------------------------------
54
- def _mostrar_efeito_aniversario(nome: str):
55
- """Exibe imediatamente efeito e mensagem central de aniversário."""
56
- try:
57
- st.balloons()
58
  except Exception:
59
- pass
60
-
61
- st.markdown(
62
- f"""
63
- <div style="
64
- display:flex;align-items:center;justify-content:center;
65
- text-align:center;margin:40px 0 20px 0;">
66
- <div style="
67
- font-size: 32px; font-weight: 800; color:#A020F0;
68
- background:linear-gradient(90deg,#FFF0F6,#F0E6FF);
69
- padding:16px 24px;border-radius:16px;box-shadow:0 4px 10px rgba(0,0,0,.08);">
70
- 🎉 Feliz Aniversário, {nome}! 🎉
71
- </div>
72
- </div>
73
- """,
74
- unsafe_allow_html=True
75
- )
76
- st.caption("Desejamos a você muitas conquistas e bons embarques ao longo do ano! 💜")
77
-
78
- # -----------------------------------------------------------------------------
79
- # Login emergencial / Autologin para testes (via Secrets)
80
- # -----------------------------------------------------------------------------
81
  def _autologin_if_allowed() -> bool:
82
- """
83
- Se DISABLE_AUTH=1 estiver definido nos Secrets,
84
- entra automático (apenas para teste/homologação).
85
- """
86
- if os.getenv("DISABLE_AUTH", "0") == "1":
87
- st.session_state.logado = True
88
- st.session_state.usuario = os.getenv("DEMO_USER", "demo")
89
- st.session_state.perfil = os.getenv("DEMO_PERFIL", "admin")
90
- st.session_state.email = os.getenv("DEMO_EMAIL", "demo@example.com")
91
- st.info("🔓 Autologin (DISABLE_AUTH=1) — apenas para teste/homologação.")
92
- return True
93
- return False
94
-
95
- def _render_emergency_login():
96
- """
97
- Login emergencial (opcional), protegido por Secrets:
98
- - ALLOW_EMERGENCY_LOGIN=1 (habilita)
99
- - EMERG_USER (usuário)
100
- - EMERG_PASS_BCRYPT (hash bcrypt da senha) OU EMERG_PASS_PLAIN (apenas para teste)
101
- """
102
- if os.getenv("ALLOW_EMERGENCY_LOGIN", "0") != "1":
103
- st.info(
104
- "Login temporariamente indisponível.\n\n"
105
- "• Para testar: defina **DISABLE_AUTH=1** em *Settings → Secrets*\n"
106
- "• Para contingência: **ALLOW_EMERGENCY_LOGIN=1**, EMERG_USER e EMERG_PASS_BCRYPT"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  )
108
- return
 
 
 
 
 
 
 
 
 
 
 
 
109
 
110
- import bcrypt # garantido no requirements
111
- st.markdown("#### Login emergencial")
112
- with st.form("emergency_login"):
113
- u = st.text_input("Usuário", value=os.getenv("EMERG_USER", "admin"))
114
- p = st.text_input("Senha", type="password")
115
- ok = st.form_submit_button("Entrar")
116
-
117
- if ok:
118
- user_ok = (u == os.getenv("EMERG_USER", "admin"))
119
- pass_hash = (os.getenv("EMERG_PASS_BCRYPT") or "").strip()
120
- pass_plain= (os.getenv("EMERG_PASS_PLAIN") or "").strip()
121
-
122
- pass_ok = False
123
- if pass_hash:
124
- try:
125
- pass_ok = bcrypt.checkpw(p.encode(), pass_hash.encode())
126
- except Exception as e:
127
- st.error(f"Validação bcrypt falhou: {e}")
128
- elif pass_plain:
129
- pass_ok = (p == pass_plain)
130
- st.warning("⚠️ EMERG_PASS_PLAIN em uso. Prefira EMERG_PASS_BCRYPT (mais seguro).")
131
- else:
132
- st.error("Defina EMERG_PASS_BCRYPT (ou EMERG_PASS_PLAIN apenas para teste).")
133
-
134
- if user_ok and pass_ok:
135
- st.session_state.logado = True
136
- st.session_state.usuario = u
137
- st.session_state.perfil = "admin"
138
- st.session_state.email = f"{u}@local"
139
- st.success("Login emergencial efetuado.")
140
- st.rerun()
141
- else:
142
- st.error("Usuário e/ou senha inválidos (emergencial).")
143
-
144
- # -----------------------------------------------------------------------------
145
- # Tela de Login
146
- # -----------------------------------------------------------------------------
147
  def login():
148
- st.subheader("🔐 Login")
149
-
150
- # ✅ Seleção do banco (se houver router)
151
- banks = list_banks()
152
- labels = [bank_label(b) for b in banks]
153
- idx_default = banks.index("prod") if "prod" in banks else 0
154
-
155
- banco_label_sel = st.selectbox("Usar banco:", labels, index=idx_default)
156
- db_choice = banks[labels.index(banco_label_sel)]
157
- set_db_choice(db_choice)
158
-
159
- # Indicação visual do banco ativo na sidebar
160
- ambiente = current_db_choice()
161
- if ambiente == "prod":
162
- badge = "🟢 Produção"
163
- elif ambiente == "test":
164
- badge = "🔴 Teste"
165
- elif ambiente == "treinamento":
166
- badge = "🔵 Treinamento"
167
- else:
168
- badge = ambiente
169
- st.sidebar.caption(f"🗄️ Banco ativo: {badge}")
170
-
171
- # 1) Autologin (debug) — sai daqui logado se DISABLE_AUTH=1
172
  if _autologin_if_allowed():
173
  return
174
 
175
- # Campos de credencial
176
- usuario_input = st.text_input("Usuário")
177
- senha_input = st.text_input("Senha", type="password")
 
 
178
 
179
- # 2) Login normal (DB)
180
- if st.button("Entrar", type="primary"):
181
- db = _get_db_session()
182
- try:
183
- usuario_db = (
184
- db.query(Usuario)
185
- .filter(Usuario.usuario == usuario_input, Usuario.ativo == True)
186
- .first()
187
- )
188
 
189
- if not usuario_db or not verificar_senha(senha_input, getattr(usuario_db, "senha", "")):
190
- st.error("❌ Usuário ou senha inválidos.")
191
- # Auditoria — tentativa inválida
192
- try:
193
- registrar_log(
194
- usuario=usuario_input or "(vazio)",
195
- acao="Tentativa de login inválida",
196
- tabela="usuarios",
197
- ambiente=current_db_choice()
198
- )
199
- except Exception:
200
- pass
201
- return
202
-
203
- # ✅ LOGIN OK — marca sessão
204
- st.session_state.logado = True
205
- st.session_state.usuario = usuario_db.usuario
206
- st.session_state.perfil = getattr(usuario_db, "perfil", "usuario") or "usuario"
207
- st.session_state.email = getattr(usuario_db, "email", None)
208
- st.session_state.nome = getattr(usuario_db, "nome", None)
209
-
210
- # 🔁 Força revalidação do quiz
211
- st.session_state.quiz_verificado = False
212
-
213
- # Auditoria de sucesso
214
- try:
215
- registrar_log(
216
- usuario=usuario_db.usuario,
217
- acao="Login realizado com sucesso",
218
- tabela="usuarios",
219
- registro_id=getattr(usuario_db, "id", None),
220
- ambiente=current_db_choice()
221
- )
222
- except Exception:
223
- pass
224
-
225
- # 🎂 Checagem de aniversário (mês/dia)
226
- try:
227
- from datetime import date as _date
228
- def _to_date_safe(val):
229
- if not val:
230
- return None
231
- if isinstance(val, _date):
232
- return val
233
- try:
234
- yy, mm, dd = map(int, str(val).split("-"))
235
- return _date(yy, mm, dd)
236
- except Exception:
237
- return None
238
-
239
- dn = _to_date_safe(getattr(usuario_db, "data_aniversario", None))
240
- hoje = _date.today()
241
- if dn and dn.month == hoje.month and dn.day == hoje.day:
242
- nome_exibir = st.session_state.get("nome") or st.session_state.get("usuario") or "Usuário"
243
- _mostrar_efeito_aniversario(nome_exibir)
244
- st.session_state["__show_birthday__"] = True
245
- except Exception:
246
- pass
247
-
248
- st.success("✅ Login realizado com sucesso!")
249
  st.rerun()
 
 
 
 
 
 
 
250
 
251
- finally:
252
- try:
253
- db.close()
254
- except Exception:
255
- pass
256
 
257
- # 3) Login emergencial (opcional) se o normal não passou
258
- if not st.session_state.get("logado"):
259
- _render_emergency_login()
260
- ``
 
 
 
 
 
 
1
  # -*- coding: utf-8 -*-
2
+ """
3
+ login.py — Sistema de autenticação seguro para Streamlit
4
+ Compatível com HuggingFace Spaces (Linux) e Windows.
5
+
6
+ Recursos:
7
+ - Login normal (usuários no banco SQLite/Postgres/MySQL)
8
+ - Hash seguro com bcrypt
9
+ - Autologin para testes (DISABLE_AUTH=1)
10
+ - Login emergencial (ALLOW_EMERGENCY_LOGIN=1 + EMERG_USER + EMERG_PASS_BCRYPT)
11
+ - Auditoria opcional via registrar_log
12
+ """
13
+
14
  import os
15
+ import bcrypt
16
  import streamlit as st
17
 
18
+ # Auditoria opcional
 
 
 
 
 
 
19
  try:
20
+ from utils_auditoria import registrar_log
21
+ _HAS_AUDIT = True
 
 
 
 
 
 
22
  except Exception:
23
+ _HAS_AUDIT = False
24
 
25
+ # Banco (ORM)
26
+ from banco import SessionLocal
27
+ from models import Usuario
28
 
29
+ # Flags
30
+ _DISABLE_AUTH = os.getenv("DISABLE_AUTH", "0") == "1"
31
+ _ALLOW_EMERG = os.getenv("ALLOW_EMERGENCY_LOGIN", "0") == "1"
32
 
33
+ _DEMO_USER = os.getenv("DEMO_USER", "demo")
34
+ _DEMO_PERFIL = os.getenv("DEMO_PERFIL", "admin")
35
+ _DEMO_EMAIL = os.getenv("DEMO_EMAIL", "demo@example.com")
36
 
 
 
37
 
38
+ # =========================================================
39
+ # 🛡️ HASH DE SENHAS
40
+ # =========================================================
41
+ def verificar_hash(senha_digitada: str, senha_hash: str) -> bool:
42
+ """Retorna True se a senha confere com o hash bcrypt."""
 
 
 
43
  try:
44
+ return bcrypt.checkpw(
45
+ senha_digitada.encode("utf-8"),
46
+ senha_hash.encode("utf-8")
47
+ )
 
 
 
 
 
 
 
 
 
48
  except Exception:
49
+ return False
50
+
51
+
52
+ # =========================================================
53
+ # 🔐 AUTOLOGIN (apenas para testes)
54
+ # =========================================================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  def _autologin_if_allowed() -> bool:
56
+ if not _DISABLE_AUTH:
57
+ return False
58
+
59
+ st.session_state.logado = True
60
+ st.session_state.usuario = _DEMO_USER
61
+ st.session_state.perfil = _DEMO_PERFIL
62
+ st.session_state.email = _DEMO_EMAIL
63
+
64
+ if _HAS_AUDIT:
65
+ try:
66
+ registrar_log(
67
+ usuario=_DEMO_USER,
68
+ acao="Autologin (DISABLE_AUTH=1)",
69
+ tabela="login",
70
+ registro_id=None
71
+ )
72
+ except Exception:
73
+ pass
74
+
75
+ st.success(f"🔓 Autologin ativo: {_DEMO_USER} ({_DEMO_PERFIL})")
76
+ return True
77
+
78
+
79
+ # =========================================================
80
+ # 🚨 LOGIN EMERGENCIAL
81
+ # =========================================================
82
+ def _try_emergency_login(usuario: str, senha: str) -> bool:
83
+ if not _ALLOW_EMERG:
84
+ return False
85
+
86
+ EMU = os.getenv("EMERG_USER")
87
+ EHP = os.getenv("EMERG_PASS_BCRYPT") # hash bcrypt
88
+
89
+ if not EMU or not EHP:
90
+ return False
91
+
92
+ if usuario.strip().lower() != EMU.strip().lower():
93
+ return False
94
+
95
+ if not verificar_hash(senha, EHP):
96
+ return False
97
+
98
+ st.session_state.logado = True
99
+ st.session_state.usuario = EMU
100
+ st.session_state.perfil = "admin"
101
+ st.session_state.email = f"{EMU}@local"
102
+
103
+ st.success("🔑 Login emergencial bem-sucedido.")
104
+
105
+ if _HAS_AUDIT:
106
+ try:
107
+ registrar_log(
108
+ usuario=EMU,
109
+ acao="Login emergencial",
110
+ tabela="login",
111
+ registro_id=None
112
+ )
113
+ except Exception:
114
+ pass
115
+
116
+ return True
117
+
118
+
119
+ # =========================================================
120
+ # 🔑 LOGIN NORMAL
121
+ # =========================================================
122
+ def _login_normal(usuario: str, senha: str) -> bool:
123
+ db = SessionLocal()
124
+ try:
125
+ row = (
126
+ db.query(Usuario)
127
+ .filter(Usuario.usuario == usuario.strip())
128
+ .first()
129
  )
130
+ except Exception:
131
+ row = None
132
+ finally:
133
+ try:
134
+ db.close()
135
+ except Exception:
136
+ pass
137
+
138
+ if not row:
139
+ return False
140
+
141
+ if not verificar_hash(senha, row.senha_hash):
142
+ return False
143
 
144
+ # OK
145
+ st.session_state.logado = True
146
+ st.session_state.usuario = row.usuario
147
+ st.session_state.perfil = row.perfil
148
+ st.session_state.email = row.email
149
+
150
+ if _HAS_AUDIT:
151
+ try:
152
+ registrar_log(
153
+ usuario=row.usuario,
154
+ acao="Login normal",
155
+ tabela="login",
156
+ registro_id=row.id
157
+ )
158
+ except Exception:
159
+ pass
160
+
161
+ return True
162
+
163
+
164
+ # =========================================================
165
+ # 🎯 TELA DE LOGIN
166
+ # =========================================================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  def login():
168
+ # 1) Autologin de teste
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  if _autologin_if_allowed():
170
  return
171
 
172
+ st.markdown("### 🔐 Login")
173
+ with st.form("form_login"):
174
+ usuario = st.text_input("Usuário")
175
+ senha = st.text_input("Senha", type="password")
176
+ btn = st.form_submit_button("Entrar")
177
 
178
+ if btn:
179
+ usuario = usuario.strip()
180
+ senha = senha.strip()
181
+
182
+ # Login normal
183
+ if _login_normal(usuario, senha):
184
+ st.rerun()
185
+ return
 
186
 
187
+ # Login emergencial
188
+ if _try_emergency_login(usuario, senha):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  st.rerun()
190
+ return
191
+
192
+ st.error("Usuário ou senha incorretos.")
193
+
194
+ # Exibe link do login emergencial apenas se habilitado
195
+ if _ALLOW_EMERG:
196
+ st.info("🔑 Login emergencial disponível.")
197
 
 
 
 
 
 
198
 
199
+ # =========================================================
200
+ # Utilitário (opcional)
201
+ # =========================================================
202
+ def logout():
203
+ st.session_state.logado = False
204
+ st.session_state.usuario = None
205
+ st.session_state.perfil = None
206
+ st.session_state.email = None
207
+ st.rerun()