Files
GTI_GERENCIADOR_FISCAL/app/auth.py
T

79 lines
2.9 KiB
Python

"""
Login por sessão. Duas fontes de credencial, checadas nessa ordem em
autenticar_credenciais() (chamada tanto por POST /login quanto,
indiretamente, por quem valida o cookie de sessão):
1. ADMIN_USER/ADMIN_PASSWORD (variáveis de ambiente) — conta única de
bootstrap, útil para o primeiro acesso (cadastrar o primeiro
funcionário em /funcionarios) e como plano B se a tabela de
funcionários ficar vazia/inacessível. Continua exigindo as duas
variáveis preenchidas (nunca autentica credencial vazia). Sempre
perfil ADMIN, não depende do banco.
2. FUNCIONARIO_PAINEL (app/db/funcionarios.py) — conta individual por
funcionário, com senha em hash (app/senha.py). Só autentica se ATIVO=1.
Perfil vem da coluna PERFIL ("ADMIN" ou "PADRAO").
A credencial em si só é usada em POST /login (app/routers/auth.py), que
troca por um cookie de sessão assinado (app/session.py) — as rotas do
painel não recebem usuário/senha a cada requisição, só o cookie.
"""
import os
import secrets
from dataclasses import dataclass
from fastapi import Depends, HTTPException, Request, status
from . import session
from .db import funcionarios as db_funcionarios
from .senha import verificar_senha
@dataclass
class UsuarioLogado:
login: str
nome: str
perfil: str # "ADMIN" ou "PADRAO"
def __str__(self) -> str:
return self.nome
@property
def eh_admin(self) -> bool:
return self.perfil == "ADMIN"
class NaoAutenticado(Exception):
def __init__(self, caminho_original: str = "/"):
self.caminho_original = caminho_original
def autenticar_credenciais(usuario: str, senha: str) -> UsuarioLogado | None:
usuario_admin = os.environ.get("ADMIN_USER", "")
senha_admin = os.environ.get("ADMIN_PASSWORD", "")
if usuario_admin and senha_admin:
usuario_ok = secrets.compare_digest(usuario, usuario_admin)
senha_ok = secrets.compare_digest(senha, senha_admin)
if usuario_ok and senha_ok:
return UsuarioLogado(login=usuario_admin, nome=usuario_admin, perfil="ADMIN")
funcionario = db_funcionarios.obter_por_login(usuario)
if funcionario and funcionario["ATIVO"] and verificar_senha(senha, funcionario["SENHA_HASH"]):
return UsuarioLogado(login=funcionario["LOGIN"], nome=funcionario["NOME"], perfil=funcionario["PERFIL"])
return None
def exigir_login(request: Request) -> UsuarioLogado:
token = request.cookies.get(session.SESSION_COOKIE_NAME)
dados = session.ler_token(token)
if dados is None:
raise NaoAutenticado(caminho_original=request.url.path)
return UsuarioLogado(login=dados["login"], nome=dados["nome"], perfil=dados["perfil"])
def exigir_admin(usuario: UsuarioLogado = Depends(exigir_login)) -> UsuarioLogado:
if not usuario.eh_admin:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Só administradores acessam esta página.")
return usuario