fix: desativar/rebaixar funcionario nao revogava sessao ativa (ate 12h)
Perfil e status ativo vinham so do cookie assinado (session.py), entao um funcionario desativado (ATIVO=0) ou rebaixado (ADMIN->PADRAO) continuava com acesso total ate o cookie expirar (ate 12h) - inclusive a /funcionarios se era admin. exigir_login (app/auth.py) agora reconfirma contra o banco a cada requisicao quando o login nao e o da conta de bootstrap (ADMIN_USER do .env, que nao tem linha em FUNCIONARIO_PAINEL e continua sempre admin via variavel de ambiente): busca db_funcionarios.obter_por_login(login); se nao existir mais ou ATIVO != 1, levanta NaoAutenticado (forca novo login); caso contrario, monta o UsuarioLogado com o PERFIL atual do banco, nao o do cookie - rebaixamento tambem passa a ter efeito imediato. Testes de regressao em tests/test_auth.py: - test_exigir_login_autentica_funcionario_ativo - test_exigir_login_revoga_sessao_ao_desativar_funcionario - test_exigir_admin_usa_perfil_atual_do_banco_nao_do_cookie - test_exigir_login_conta_bootstrap_nao_consulta_banco Ajustou tambem test_exigir_admin_rejeita_padrao (usava um login fake sem linha em FUNCIONARIO_PAINEL, que agora conflita com a checagem nova - passou a usar a fixture funcionario_teste). Suite inteira roda 147 passed apos a mudanca. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
ba8ffaede7
commit
e5f5d4211c
+20
-1
@@ -65,11 +65,30 @@ def autenticar_credenciais(usuario: str, senha: str) -> UsuarioLogado | None:
|
|||||||
|
|
||||||
|
|
||||||
def exigir_login(request: Request) -> UsuarioLogado:
|
def exigir_login(request: Request) -> UsuarioLogado:
|
||||||
|
"""
|
||||||
|
Decodifica o cookie de sessão e, para logins que existem em
|
||||||
|
FUNCIONARIO_PAINEL, reconfirma ATIVO/PERFIL contra o banco a cada
|
||||||
|
requisição — o cookie assinado tem validade de até 12h e não é
|
||||||
|
revogado, então sem essa checagem desativar ou rebaixar um funcionário
|
||||||
|
não teria efeito imediato (continuaria com acesso até o cookie
|
||||||
|
expirar). A conta de bootstrap (ADMIN_USER do .env) não tem linha em
|
||||||
|
FUNCIONARIO_PAINEL e continua sempre admin via variável de ambiente,
|
||||||
|
sem consulta ao banco.
|
||||||
|
"""
|
||||||
token = request.cookies.get(session.SESSION_COOKIE_NAME)
|
token = request.cookies.get(session.SESSION_COOKIE_NAME)
|
||||||
dados = session.ler_token(token)
|
dados = session.ler_token(token)
|
||||||
if dados is None:
|
if dados is None:
|
||||||
raise NaoAutenticado(caminho_original=request.url.path)
|
raise NaoAutenticado(caminho_original=request.url.path)
|
||||||
return UsuarioLogado(login=dados["login"], nome=dados["nome"], perfil=dados["perfil"])
|
|
||||||
|
usuario_admin = os.environ.get("ADMIN_USER", "")
|
||||||
|
if usuario_admin and dados["login"] == usuario_admin:
|
||||||
|
return UsuarioLogado(login=dados["login"], nome=dados["nome"], perfil="ADMIN")
|
||||||
|
|
||||||
|
funcionario = db_funcionarios.obter_por_login(dados["login"])
|
||||||
|
if funcionario is None or not funcionario["ATIVO"]:
|
||||||
|
raise NaoAutenticado(caminho_original=request.url.path)
|
||||||
|
|
||||||
|
return UsuarioLogado(login=funcionario["LOGIN"], nome=funcionario["NOME"], perfil=funcionario["PERFIL"])
|
||||||
|
|
||||||
|
|
||||||
def exigir_admin(usuario: UsuarioLogado = Depends(exigir_login)) -> UsuarioLogado:
|
def exigir_admin(usuario: UsuarioLogado = Depends(exigir_login)) -> UsuarioLogado:
|
||||||
|
|||||||
+41
-2
@@ -63,8 +63,11 @@ def test_exigir_admin_aceita_admin():
|
|||||||
assert resposta.status_code == 200
|
assert resposta.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
def test_exigir_admin_rejeita_padrao():
|
def test_exigir_admin_rejeita_padrao(funcionario_teste):
|
||||||
resposta = client.get("/so-admin", cookies=_cookie_sessao("fulano", "Fulano", "PADRAO"))
|
# exigir_login agora reconfirma o perfil contra o banco, então o login
|
||||||
|
# precisa existir de fato em FUNCIONARIO_PAINEL (funcionario_teste é
|
||||||
|
# cadastrado com PERFIL="PADRAO" — ver fixture abaixo).
|
||||||
|
resposta = client.get("/so-admin", cookies=_cookie_sessao(funcionario_teste, "Fulano", "PADRAO"))
|
||||||
assert resposta.status_code == 403
|
assert resposta.status_code == 403
|
||||||
|
|
||||||
|
|
||||||
@@ -123,3 +126,39 @@ def test_autenticar_credenciais_funcionario_inativo(funcionario_teste):
|
|||||||
|
|
||||||
def test_autenticar_credenciais_login_inexistente():
|
def test_autenticar_credenciais_login_inexistente():
|
||||||
assert autenticar_credenciais("nao-existe-esse-login", "qualquer") is None
|
assert autenticar_credenciais("nao-existe-esse-login", "qualquer") is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_exigir_login_autentica_funcionario_ativo(funcionario_teste):
|
||||||
|
resposta = client.get("/protegido", cookies=_cookie_sessao(funcionario_teste, "Fulano de Teste", "PADRAO"))
|
||||||
|
assert resposta.status_code == 200
|
||||||
|
assert resposta.json() == {"usuario": "Fulano de Teste", "perfil": "PADRAO"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_exigir_login_revoga_sessao_ao_desativar_funcionario(funcionario_teste):
|
||||||
|
# Cookie "válido" (assinatura e validade OK) gerado antes de desativar.
|
||||||
|
cookies = _cookie_sessao(funcionario_teste, "Fulano de Teste", "PADRAO")
|
||||||
|
assert client.get("/protegido", cookies=cookies).status_code == 200
|
||||||
|
|
||||||
|
funcionario = db_funcionarios.obter_por_login(funcionario_teste)
|
||||||
|
db_funcionarios.alternar_ativo(funcionario["ID"]) # desativa
|
||||||
|
|
||||||
|
resposta = client.get("/protegido", cookies=cookies)
|
||||||
|
assert resposta.status_code == 401
|
||||||
|
|
||||||
|
|
||||||
|
def test_exigir_admin_usa_perfil_atual_do_banco_nao_do_cookie(funcionario_teste):
|
||||||
|
# Cookie diz ADMIN, mas o funcionário no banco é PADRAO (ex: foi
|
||||||
|
# rebaixado depois de logar) — exigir_admin precisa rejeitar.
|
||||||
|
cookies = _cookie_sessao(funcionario_teste, "Fulano de Teste", "ADMIN")
|
||||||
|
resposta = client.get("/so-admin", cookies=cookies)
|
||||||
|
assert resposta.status_code == 403
|
||||||
|
|
||||||
|
|
||||||
|
def test_exigir_login_conta_bootstrap_nao_consulta_banco(monkeypatch):
|
||||||
|
def _falha_se_chamado(login):
|
||||||
|
raise AssertionError("exigir_login não deveria consultar o banco para a conta de bootstrap")
|
||||||
|
|
||||||
|
monkeypatch.setattr(db_funcionarios, "obter_por_login", _falha_se_chamado)
|
||||||
|
resposta = client.get("/protegido", cookies=_cookie_sessao("admin", "Admin", "ADMIN"))
|
||||||
|
assert resposta.status_code == 200
|
||||||
|
assert resposta.json() == {"usuario": "Admin", "perfil": "ADMIN"}
|
||||||
|
|||||||
Reference in New Issue
Block a user