IOI-RUN / login.py
Roudrigus's picture
Update login.py
d79bb40 verified
raw
history blame
5.46 kB
# -*- coding: utf-8 -*-
"""
login.py — Sistema de autenticação seguro para Streamlit
Compatível com HuggingFace Spaces (Linux) e Windows.
Recursos:
- Login normal (usuários no banco SQLite/Postgres/MySQL)
- Hash seguro com bcrypt
- Autologin para testes (DISABLE_AUTH=1)
- Login emergencial (ALLOW_EMERGENCY_LOGIN=1 + EMERG_USER + EMERG_PASS_BCRYPT)
- Auditoria opcional via registrar_log
"""
import os
import bcrypt
import streamlit as st
# Auditoria opcional
try:
from utils_auditoria import registrar_log
_HAS_AUDIT = True
except Exception:
_HAS_AUDIT = False
# Banco (ORM)
from banco import SessionLocal
from models import Usuario
# Flags
_DISABLE_AUTH = os.getenv("DISABLE_AUTH", "0") == "1"
_ALLOW_EMERG = os.getenv("ALLOW_EMERGENCY_LOGIN", "0") == "1"
_DEMO_USER = os.getenv("DEMO_USER", "demo")
_DEMO_PERFIL = os.getenv("DEMO_PERFIL", "admin")
_DEMO_EMAIL = os.getenv("DEMO_EMAIL", "demo@example.com")
# =========================================================
# 🛡️ HASH DE SENHAS
# =========================================================
def verificar_hash(senha_digitada: str, senha_hash: str) -> bool:
"""Retorna True se a senha confere com o hash bcrypt."""
try:
return bcrypt.checkpw(
senha_digitada.encode("utf-8"),
senha_hash.encode("utf-8")
)
except Exception:
return False
# =========================================================
# 🔐 AUTOLOGIN (apenas para testes)
# =========================================================
def _autologin_if_allowed() -> bool:
if not _DISABLE_AUTH:
return False
st.session_state.logado = True
st.session_state.usuario = _DEMO_USER
st.session_state.perfil = _DEMO_PERFIL
st.session_state.email = _DEMO_EMAIL
if _HAS_AUDIT:
try:
registrar_log(
usuario=_DEMO_USER,
acao="Autologin (DISABLE_AUTH=1)",
tabela="login",
registro_id=None
)
except Exception:
pass
st.success(f"🔓 Autologin ativo: {_DEMO_USER} ({_DEMO_PERFIL})")
return True
# =========================================================
# 🚨 LOGIN EMERGENCIAL
# =========================================================
def _try_emergency_login(usuario: str, senha: str) -> bool:
if not _ALLOW_EMERG:
return False
EMU = os.getenv("EMERG_USER")
EHP = os.getenv("EMERG_PASS_BCRYPT") # hash bcrypt
if not EMU or not EHP:
return False
if usuario.strip().lower() != EMU.strip().lower():
return False
if not verificar_hash(senha, EHP):
return False
st.session_state.logado = True
st.session_state.usuario = EMU
st.session_state.perfil = "admin"
st.session_state.email = f"{EMU}@local"
st.success("🔑 Login emergencial bem-sucedido.")
if _HAS_AUDIT:
try:
registrar_log(
usuario=EMU,
acao="Login emergencial",
tabela="login",
registro_id=None
)
except Exception:
pass
return True
# =========================================================
# 🔑 LOGIN NORMAL
# =========================================================
def _login_normal(usuario: str, senha: str) -> bool:
db = SessionLocal()
try:
row = (
db.query(Usuario)
.filter(Usuario.usuario == usuario.strip())
.first()
)
except Exception:
row = None
finally:
try:
db.close()
except Exception:
pass
if not row:
return False
if not verificar_hash(senha, row.senha_hash):
return False
# OK
st.session_state.logado = True
st.session_state.usuario = row.usuario
st.session_state.perfil = row.perfil
st.session_state.email = row.email
if _HAS_AUDIT:
try:
registrar_log(
usuario=row.usuario,
acao="Login normal",
tabela="login",
registro_id=row.id
)
except Exception:
pass
return True
# =========================================================
# 🎯 TELA DE LOGIN
# =========================================================
def login():
# 1) Autologin de teste
if _autologin_if_allowed():
return
st.markdown("### 🔐 Login")
with st.form("form_login"):
usuario = st.text_input("Usuário")
senha = st.text_input("Senha", type="password")
btn = st.form_submit_button("Entrar")
if btn:
usuario = usuario.strip()
senha = senha.strip()
# Login normal
if _login_normal(usuario, senha):
st.rerun()
return
# Login emergencial
if _try_emergency_login(usuario, senha):
st.rerun()
return
st.error("Usuário ou senha incorretos.")
# Exibe link do login emergencial apenas se habilitado
if _ALLOW_EMERG:
st.info("🔑 Login emergencial disponível.")
# =========================================================
# Utilitário (opcional)
# =========================================================
def logout():
st.session_state.logado = False
st.session_state.usuario = None
st.session_state.perfil = None
st.session_state.email = None
st.rerun()