# -*- coding: utf-8 -*- import os import streamlit as st from banco import SessionLocal from models import Usuario from utils_seguranca import verificar_senha from utils_auditoria import registrar_log # 🔀 Roteador de banco (Produção/Teste/Treinamento) — opcional # Se não houver db_router.py, usamos fallback suave. try: from db_router import ( set_db_choice, current_db_choice, list_banks, bank_label, get_session_for_current_db, # opcional — se existir, usamos a sessão certa ) _HAS_ROUTER = True except Exception: _HAS_ROUTER = False def set_db_choice(choice: str): st.session_state["__db_choice__"] = (choice or "prod").lower() def current_db_choice() -> str: return st.session_state.get("__db_choice__", "prod") def list_banks(): return ["prod", "test"] def bank_label(choice: str) -> str: return {"prod": "Banco 1 (📗 Produção)", "test": "Banco 2 (📕 Teste)"}.get(choice, choice) # ----------------------------------------------------------------------------- # Sessão de banco por ambiente # ----------------------------------------------------------------------------- def _get_db_session(): """ Se o router tiver uma fábrica de sessão por banco, usa ela. Caso contrário, usa SessionLocal() padrão. """ try: if _HAS_ROUTER and callable(get_session_for_current_db): # type: ignore[name-defined] return get_session_for_current_db() # db per ambiente except Exception: pass return SessionLocal() # ----------------------------------------------------------------------------- # Efeito de aniversário (visual) # ----------------------------------------------------------------------------- def _mostrar_efeito_aniversario(nome: str): """Exibe imediatamente efeito e mensagem central de aniversário.""" try: st.balloons() except Exception: pass st.markdown( f"""
🎉 Feliz Aniversário, {nome}! 🎉
""", unsafe_allow_html=True ) st.caption("Desejamos a você muitas conquistas e bons embarques ao longo do ano! 💜") # ----------------------------------------------------------------------------- # Login emergencial / Autologin para testes (via Secrets) # ----------------------------------------------------------------------------- def _autologin_if_allowed() -> bool: """ Se DISABLE_AUTH=1 estiver definido nos Secrets, entra automático (apenas para teste/homologação). """ if os.getenv("DISABLE_AUTH", "0") == "1": st.session_state.logado = True st.session_state.usuario = os.getenv("DEMO_USER", "demo") st.session_state.perfil = os.getenv("DEMO_PERFIL", "admin") st.session_state.email = os.getenv("DEMO_EMAIL", "demo@example.com") st.info("🔓 Autologin (DISABLE_AUTH=1) — apenas para teste/homologação.") return True return False def _render_emergency_login(): """ Login emergencial (opcional), protegido por Secrets: - ALLOW_EMERGENCY_LOGIN=1 (habilita) - EMERG_USER (usuário) - EMERG_PASS_BCRYPT (hash bcrypt da senha) OU EMERG_PASS_PLAIN (apenas para teste) """ if os.getenv("ALLOW_EMERGENCY_LOGIN", "0") != "1": st.info( "Login temporariamente indisponível.\n\n" "• Para testar: defina **DISABLE_AUTH=1** em *Settings → Secrets*\n" "• Para contingência: **ALLOW_EMERGENCY_LOGIN=1**, EMERG_USER e EMERG_PASS_BCRYPT" ) return import bcrypt # garantido no requirements st.markdown("#### Login emergencial") with st.form("emergency_login"): u = st.text_input("Usuário", value=os.getenv("EMERG_USER", "admin")) p = st.text_input("Senha", type="password") ok = st.form_submit_button("Entrar") if ok: user_ok = (u == os.getenv("EMERG_USER", "admin")) pass_hash = (os.getenv("EMERG_PASS_BCRYPT") or "").strip() pass_plain= (os.getenv("EMERG_PASS_PLAIN") or "").strip() pass_ok = False if pass_hash: try: pass_ok = bcrypt.checkpw(p.encode(), pass_hash.encode()) except Exception as e: st.error(f"Validação bcrypt falhou: {e}") elif pass_plain: pass_ok = (p == pass_plain) st.warning("⚠️ EMERG_PASS_PLAIN em uso. Prefira EMERG_PASS_BCRYPT (mais seguro).") else: st.error("Defina EMERG_PASS_BCRYPT (ou EMERG_PASS_PLAIN apenas para teste).") if user_ok and pass_ok: st.session_state.logado = True st.session_state.usuario = u st.session_state.perfil = "admin" st.session_state.email = f"{u}@local" st.success("Login emergencial efetuado.") st.rerun() else: st.error("Usuário e/ou senha inválidos (emergencial).") # ----------------------------------------------------------------------------- # Tela de Login # ----------------------------------------------------------------------------- def login(): st.subheader("🔐 Login") # ✅ Seleção do banco (se houver router) banks = list_banks() labels = [bank_label(b) for b in banks] idx_default = banks.index("prod") if "prod" in banks else 0 banco_label_sel = st.selectbox("Usar banco:", labels, index=idx_default) db_choice = banks[labels.index(banco_label_sel)] set_db_choice(db_choice) # Indicação visual do banco ativo na sidebar ambiente = current_db_choice() if ambiente == "prod": badge = "🟢 Produção" elif ambiente == "test": badge = "🔴 Teste" elif ambiente == "treinamento": badge = "🔵 Treinamento" else: badge = ambiente st.sidebar.caption(f"🗄️ Banco ativo: {badge}") # 1) Autologin (debug) — sai daqui logado se DISABLE_AUTH=1 if _autologin_if_allowed(): return # Campos de credencial usuario_input = st.text_input("Usuário") senha_input = st.text_input("Senha", type="password") # 2) Login normal (DB) if st.button("Entrar", type="primary"): db = _get_db_session() try: usuario_db = ( db.query(Usuario) .filter(Usuario.usuario == usuario_input, Usuario.ativo == True) .first() ) if not usuario_db or not verificar_senha(senha_input, getattr(usuario_db, "senha", "")): st.error("❌ Usuário ou senha inválidos.") # Auditoria — tentativa inválida try: registrar_log( usuario=usuario_input or "(vazio)", acao="Tentativa de login inválida", tabela="usuarios", ambiente=current_db_choice() ) except Exception: pass return # ✅ LOGIN OK — marca sessão st.session_state.logado = True st.session_state.usuario = usuario_db.usuario st.session_state.perfil = getattr(usuario_db, "perfil", "usuario") or "usuario" st.session_state.email = getattr(usuario_db, "email", None) st.session_state.nome = getattr(usuario_db, "nome", None) # 🔁 Força revalidação do quiz st.session_state.quiz_verificado = False # Auditoria de sucesso try: registrar_log( usuario=usuario_db.usuario, acao="Login realizado com sucesso", tabela="usuarios", registro_id=getattr(usuario_db, "id", None), ambiente=current_db_choice() ) except Exception: pass # 🎂 Checagem de aniversário (mês/dia) try: from datetime import date as _date def _to_date_safe(val): if not val: return None if isinstance(val, _date): return val try: yy, mm, dd = map(int, str(val).split("-")) return _date(yy, mm, dd) except Exception: return None dn = _to_date_safe(getattr(usuario_db, "data_aniversario", None)) hoje = _date.today() if dn and dn.month == hoje.month and dn.day == hoje.day: nome_exibir = st.session_state.get("nome") or st.session_state.get("usuario") or "Usuário" _mostrar_efeito_aniversario(nome_exibir) st.session_state["__show_birthday__"] = True except Exception: pass st.success("✅ Login realizado com sucesso!") st.rerun() finally: try: db.close() except Exception: pass # 3) Login emergencial (opcional) se o normal não passou if not st.session_state.get("logado"): _render_emergency_login() ``