Roudrigus commited on
Commit
44b7086
·
verified ·
1 Parent(s): f35ae73

Update login.py

Browse files
Files changed (1) hide show
  1. login.py +260 -175
login.py CHANGED
@@ -1,175 +1,260 @@
1
-
2
- # -*- coding: utf-8 -*-
3
- import streamlit as st
4
- from banco import SessionLocal
5
- from models import Usuario
6
- from utils_seguranca import verificar_senha
7
- from utils_auditoria import registrar_log
8
-
9
- # 🔀 Roteador de banco (Produção/Teste/Treinamento)
10
- # Observação: este import assume que db_router.py está na raiz do projeto.
11
- # Se ainda não existir, usamos um fallback suave (default 'prod').
12
- try:
13
- from db_router import set_db_choice, current_db_choice, list_banks, bank_label
14
- _HAS_ROUTER = True
15
- except Exception:
16
- _HAS_ROUTER = False
17
- def set_db_choice(choice: str):
18
- st.session_state["__db_choice__"] = (choice or "prod").lower()
19
- def current_db_choice() -> str:
20
- return st.session_state.get("__db_choice__", "prod")
21
- def list_banks():
22
- return ["prod", "test"] # fallback básico
23
- def bank_label(choice: str) -> str:
24
- return {"prod": "Banco 1 (📗 Produção)", "test": "Banco 2 (📕 Teste)"}.get(choice, choice)
25
-
26
-
27
- def _mostrar_efeito_aniversario(nome: str):
28
- """Exibe imediatamente efeito e mensagem central de aniversário."""
29
- # Efeito visual (confete e balões)
30
- st.snow()
31
- st.balloons()
32
-
33
- # Mensagem centralizada
34
- st.markdown(
35
- f"""
36
- <div style="
37
- display:flex;align-items:center;justify-content:center;
38
- text-align:center;margin:40px 0 20px 0;">
39
- <div style="
40
- font-size: 32px; font-weight: 800; color:#A020F0;
41
- background:linear-gradient(90deg,#FFF0F6,#F0E6FF);
42
- padding:16px 24px;border-radius:16px;box-shadow:0 4px 10px rgba(0,0,0,.08);">
43
- 🎉 Feliz Aniversário, {nome}! 🎉
44
- </div>
45
- </div>
46
- """,
47
- unsafe_allow_html=True
48
- )
49
- st.caption("Desejamos a você muitas conquistas e bons embarques ao longo do ano! 💜")
50
-
51
-
52
- def login():
53
- st.subheader("🔐 Login")
54
-
55
- # Seleção do banco (dinâmica) armazenado em sessão para toda a navegação
56
- # Mantém experiência simples e clara para o usuário.
57
- banks = list_banks() # ex.: ['prod', 'test', 'treinamento']
58
- labels = [bank_label(b) for b in banks] # rótulos amigáveis para UI
59
- idx_default = banks.index("prod") if "prod" in banks else 0
60
-
61
- banco_label = st.selectbox("Usar banco:", labels, index=idx_default)
62
- db_choice = banks[labels.index(banco_label)]
63
- set_db_choice(db_choice)
64
-
65
- # (Opcional) Indicação visual do banco ativo na sidebar
66
- # Usando ícones diferentes para cada ambiente:
67
- ambiente = current_db_choice()
68
- if ambiente == "prod":
69
- badge = "🟢 Produção"
70
- elif ambiente == "test":
71
- badge = "🔴 Teste"
72
- elif ambiente == "treinamento":
73
- badge = "🔵 Treinamento"
74
- else:
75
- badge = ambiente
76
- st.sidebar.caption(f"🗄️ Banco ativo: {badge}")
77
-
78
- # Campos de credencial
79
- usuario_input = st.text_input("Usuário")
80
- senha_input = st.text_input("Senha", type="password")
81
-
82
- # 🔘 Botão de entrada (mantendo seu fluxo)
83
- if st.button("Entrar", type="primary"):
84
- db = SessionLocal()
85
- try:
86
- usuario_db = (
87
- db.query(Usuario)
88
- .filter(
89
- Usuario.usuario == usuario_input,
90
- Usuario.ativo == True
91
- )
92
- .first()
93
- )
94
-
95
- if not usuario_db or not verificar_senha(senha_input, usuario_db.senha):
96
- st.error("❌ Usuário ou senha inválidos.")
97
-
98
- # 📝 Auditoria — registra também o ambiente atual (prod/test/treinamento)
99
- try:
100
- registrar_log(
101
- usuario=usuario_input,
102
- acao="Tentativa de login inválida",
103
- tabela="usuarios",
104
- ambiente=current_db_choice()
105
- )
106
- except Exception:
107
- # Não quebra o fluxo se auditoria falhar
108
- pass
109
-
110
- return
111
-
112
- # ✅ LOGIN OK
113
- st.session_state.logado = True
114
- st.session_state.usuario = usuario_db.usuario
115
- st.session_state.perfil = usuario_db.perfil
116
-
117
- # ✅ Armazenar e-mail e, se disponível, nome (para exibição na UI)
118
- # Obs.: getattr evita erro caso o campo não exista ou esteja nulo.
119
- st.session_state.email = getattr(usuario_db, "email", None)
120
- st.session_state.nome = getattr(usuario_db, "nome", None)
121
-
122
- # 🔁 IMPORTANTE: força revalidação do quiz
123
- st.session_state.quiz_verificado = False
124
-
125
- # 📝 Auditoria de sucesso — registra o ambiente
126
- try:
127
- registrar_log(
128
- usuario=usuario_db.usuario,
129
- acao="Login realizado com sucesso",
130
- tabela="usuarios",
131
- registro_id=usuario_db.id,
132
- ambiente=current_db_choice()
133
- )
134
- except Exception:
135
- pass
136
-
137
- # 🎂 NOVO: checagem de data de aniversário (mês/dia), compatível com Date e ISO string
138
- try:
139
- from datetime import date as _date
140
-
141
- def _to_date_safe(val):
142
- if not val:
143
- return None
144
- # já é 'date'?
145
- if isinstance(val, _date):
146
- return val
147
- # tenta converter de string ISO "YYYY-MM-DD"
148
- try:
149
- yy, mm, dd = map(int, str(val).split("-"))
150
- return _date(yy, mm, dd)
151
- except Exception:
152
- return None
153
-
154
- dn = _to_date_safe(getattr(usuario_db, "data_aniversario", None))
155
- hoje = _date.today()
156
-
157
- if dn and (dn.month == hoje.month and dn.day == hoje.day):
158
- # Mostra efeito imediatamente (antes do rerun)
159
- nome_exibir = st.session_state.get("nome") or st.session_state.get("usuario") or "Usuário"
160
- _mostrar_efeito_aniversario(nome_exibir)
161
-
162
- # Opcional: também sinaliza para main() caso queira reapresentar em outra área
163
- st.session_state["__show_birthday__"] = True
164
- except Exception:
165
- # Não impede o login se algo falhar nessa checagem
166
- pass
167
-
168
- st.success("✅ Login realizado com sucesso!")
169
- st.rerun()
170
-
171
- finally:
172
- db.close()
173
-
174
-
175
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ ``