diff --git a/app/auth.py b/app/auth.py new file mode 100644 index 0000000..9bbbc26 --- /dev/null +++ b/app/auth.py @@ -0,0 +1,27 @@ +""" +Login simples via HTTP Basic Auth, usuário/senha vindos de variável de +ambiente. Sem cadastro de usuários, sem perfis, sem sessão/cookie. +""" +import os +import secrets + +from fastapi import Depends, HTTPException, status +from fastapi.security import HTTPBasic, HTTPBasicCredentials + +_security = HTTPBasic() + + +def exigir_login(credenciais: HTTPBasicCredentials = Depends(_security)) -> str: + usuario_esperado = os.environ.get("ADMIN_USER", "") + senha_esperada = os.environ.get("ADMIN_PASSWORD", "") + + usuario_ok = secrets.compare_digest(credenciais.username, usuario_esperado) + senha_ok = secrets.compare_digest(credenciais.password, senha_esperada) + + if not (usuario_ok and senha_ok): + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Usuário ou senha inválidos.", + headers={"WWW-Authenticate": "Basic"}, + ) + return credenciais.username diff --git a/tests/test_auth.py b/tests/test_auth.py new file mode 100644 index 0000000..20e1f84 --- /dev/null +++ b/tests/test_auth.py @@ -0,0 +1,38 @@ +import base64 + +from fastapi import Depends, FastAPI +from fastapi.testclient import TestClient + +from app.auth import exigir_login + +app_teste = FastAPI() + + +@app_teste.get("/protegido") +def rota_protegida(usuario: str = Depends(exigir_login)): + return {"usuario": usuario} + + +client = TestClient(app_teste) + + +def _basic(usuario: str, senha: str) -> dict: + token = base64.b64encode(f"{usuario}:{senha}".encode()).decode() + return {"Authorization": f"Basic {token}"} + + +def test_sem_credenciais_retorna_401(): + resposta = client.get("/protegido") + assert resposta.status_code == 401 + + +def test_credenciais_erradas_retorna_401(): + resposta = client.get("/protegido", headers=_basic("admin", "senha_errada")) + assert resposta.status_code == 401 + + +def test_credenciais_corretas_retorna_200(): + # ADMIN_USER/ADMIN_PASSWORD vêm de tests/conftest.py ("admin"/"teste123") + resposta = client.get("/protegido", headers=_basic("admin", "teste123")) + assert resposta.status_code == 200 + assert resposta.json() == {"usuario": "admin"}