diff --git a/app/auth.py b/app/auth.py index e46c009..4126bec 100644 --- a/app/auth.py +++ b/app/auth.py @@ -65,11 +65,30 @@ def autenticar_credenciais(usuario: str, senha: str) -> UsuarioLogado | None: 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) 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"]) + + 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: diff --git a/tests/test_auth.py b/tests/test_auth.py index f2386cb..61fae59 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -63,8 +63,11 @@ def test_exigir_admin_aceita_admin(): assert resposta.status_code == 200 -def test_exigir_admin_rejeita_padrao(): - resposta = client.get("/so-admin", cookies=_cookie_sessao("fulano", "Fulano", "PADRAO")) +def test_exigir_admin_rejeita_padrao(funcionario_teste): + # 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 @@ -123,3 +126,39 @@ def test_autenticar_credenciais_funcionario_inativo(funcionario_teste): def test_autenticar_credenciais_login_inexistente(): 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"}