fix: botao Sair quebrado em /clientes, /log e /gerador-chave (csrf_token vazio)
base.html usava {{ csrf_token }}, variavel que so algumas rotas passam
explicitamente no contexto do template. Em /clientes (GET), /log e
/gerador-chave a variavel nunca era passada, entao o campo oculto do
formulario de logout renderizava com value="" e exigir_csrf (app/csrf.py)
rejeitava o POST /logout com 403 - o usuario nao conseguia clicar em
'Sair' logo apos logar, ja que /clientes e para onde todo login cai.
Corrige na raiz: usa {{ csrf_token or request.state.csrf_token }}, caindo
para o valor que csrf_cookie_middleware (app/main.py) ja popula em toda
requisicao quando a rota nao passar csrf_token explicitamente. Protege
tambem qualquer rota futura que esqueca de passar a variavel.
Testes de regressao em tests/test_main.py: confirma que GET /clientes
renderiza um csrf_token nao vazio no formulario de logout, e que
POST /logout funciona (303) apos visitar /clientes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
a71b990a3c
commit
aa1c528197
@@ -35,7 +35,7 @@
|
||||
{% if request.state.usuario %}
|
||||
<a href="/minha-conta">Minha conta</a>
|
||||
<form method="post" action="/logout" style="display:inline;margin:0">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token or request.state.csrf_token }}">
|
||||
<button type="submit" style="margin:0;padding:4px 10px;background:transparent;border:1px solid #3a3a3e;color:#f4f4f5;font-weight:400">Sair</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
@@ -62,3 +62,60 @@ def test_csrf_libera_post_quando_token_bate_com_cookie(monkeypatch):
|
||||
"/clientes/1/excluir", cookies=cookies, data={"csrf_token": token_cookie}, follow_redirects=False
|
||||
)
|
||||
assert resposta.status_code == 303
|
||||
|
||||
|
||||
def test_pagina_clientes_tem_csrf_token_preenchido_no_botao_sair(monkeypatch):
|
||||
"""
|
||||
Regressão: GET /clientes (a página onde todo login cai) não passava
|
||||
csrf_token no contexto do template, então o formulário de logout em
|
||||
base.html renderizava com value="" e POST /logout era rejeitado com
|
||||
403 por app/csrf.py. base.html agora usa
|
||||
"{{ csrf_token or request.state.csrf_token }}" como fallback.
|
||||
"""
|
||||
from app.routers import clientes as router_clientes
|
||||
|
||||
monkeypatch.setattr(
|
||||
router_clientes.db_clientes,
|
||||
"listar_clientes",
|
||||
lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc": [],
|
||||
)
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 0)
|
||||
|
||||
client.cookies.clear()
|
||||
resposta = client.get("/clientes", cookies=_cookie_sessao_admin())
|
||||
assert resposta.status_code == 200
|
||||
assert 'name="csrf_token" value=""' not in resposta.text
|
||||
|
||||
import re
|
||||
|
||||
match = re.search(r'name="csrf_token" value="([^"]*)"', resposta.text)
|
||||
assert match is not None
|
||||
assert len(match.group(1)) > 0
|
||||
|
||||
|
||||
def test_logout_funciona_depois_de_visitar_clientes(monkeypatch):
|
||||
"""
|
||||
Regressão complementar: simula o fluxo real — visitar /clientes (onde
|
||||
o cookie csrf_token é definido pelo middleware) e então enviar
|
||||
POST /logout usando o csrf_token real do cookie do navegador, sem
|
||||
passar csrf_token manualmente no contexto do template.
|
||||
"""
|
||||
from app.routers import clientes as router_clientes
|
||||
|
||||
monkeypatch.setattr(
|
||||
router_clientes.db_clientes,
|
||||
"listar_clientes",
|
||||
lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc": [],
|
||||
)
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 0)
|
||||
|
||||
client.cookies.clear()
|
||||
client.cookies.update(_cookie_sessao_admin())
|
||||
resposta_clientes = client.get("/clientes")
|
||||
assert resposta_clientes.status_code == 200
|
||||
|
||||
token_cookie = client.cookies.get("csrf_token")
|
||||
assert token_cookie
|
||||
|
||||
resposta_logout = client.post("/logout", data={"csrf_token": token_cookie}, follow_redirects=False)
|
||||
assert resposta_logout.status_code == 303
|
||||
|
||||
Reference in New Issue
Block a user