From 2f01dd929c09f3c7ebc5d6bc73ea21c093dd9763 Mon Sep 17 00:00:00 2001 From: joaohortsenado <100957576+joaohortsenado@users.noreply.github.com> Date: Mon, 4 May 2026 17:55:52 -0400 Subject: [PATCH] =?UTF-8?q?Corrige=20polling=20excessivo=20e=20bloqueia=20?= =?UTF-8?q?n=C3=A3o-votantes=20em=20/voto-individual/=20(#3831)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Corrige polling excessivo em /voto-individual/ - Adiciona @login_required em votante_view para que usuários anônimos sejam redirecionados ao login em vez de receberem o template com auto-reload. - Corrige bug do relógio: setInterval recursivo dentro de startTime() acumulava timers exponencialmente. Trocado por setTimeout, mantendo apenas um timer pendente. - Move o script de reload/relógio para dentro do bloco {% if not error_message %} para que a variante de erro não agende reloads de 30s. Reduz drasticamente o volume de requisições que disparavam o rate limiter (RATE_LIMITER_RATE=35/m) quando vários parlamentares mantinham a tela aberta atrás do mesmo IP NAT. Co-Authored-By: Claude Opus 4.7 (1M context) * Bloqueia /voto-individual/ para usuários sem Votante/can_vote Antes, usuários autenticados sem registro de Votante ou sem a permissão parlamentares.can_vote recebiam HTTP 200 com um template de erro (e, antes do commit anterior, ainda traziam o JS de auto- reload). Agora a view exige as duas condições no início e retorna HTTP 403 caso falhe: - @permission_required('parlamentares.can_vote', raise_exception=True) garante a permissão (com raise_exception para 403 ao invés do redirect padrão pra LOGIN_URL). - Checagem inline de Votante.objects.filter(user=...).exists() pega o caso de superusers (que passam pelo bypass automático de permissões do Django mas não têm cadastro de Votante) e de qualquer custom group que conceda can_vote sem o cadastro. Mantém HTTP 200 com mensagem amigável para falhas de estado que um votante legítimo pode encontrar (nenhuma matéria aberta, parlamentar não presente na sessão). Co-Authored-By: Claude Opus 4.7 (1M context) --------- Co-authored-by: joao Co-authored-by: Claude Opus 4.7 (1M context) --- sapl/painel/views.py | 45 ++++++++-------------- sapl/templates/painel/voto_individual.html | 14 ++++--- 2 files changed, 23 insertions(+), 36 deletions(-) diff --git a/sapl/painel/views.py b/sapl/painel/views.py index cea4e2870..d488294b2 100644 --- a/sapl/painel/views.py +++ b/sapl/painel/views.py @@ -3,8 +3,9 @@ import json import logging from django.contrib import messages -from django.contrib.auth.decorators import user_passes_test -from django.core.exceptions import ObjectDoesNotExist +from django.contrib.auth.decorators import (login_required, permission_required, + user_passes_test) +from django.core.exceptions import ObjectDoesNotExist, PermissionDenied from django.urls import reverse from django.db.models import Q from django.http import HttpResponse, JsonResponse @@ -199,39 +200,23 @@ def can_vote(context, context_vars, request): return context, context_vars +@login_required +@permission_required('parlamentares.can_vote', raise_exception=True) def votante_view(request): logger = logging.getLogger(__name__) - username = request.user.username if request.user.is_authenticated else 'AnonymousUser' - - # Pega o votante relacionado ao usuário - template_name = 'painel/voto_individual.html' - context = {} - context_vars = {} + username = request.user.username - try: - logger.debug(f'user={username}. Tentando obter objeto Votante com user={request.user}.') - if not request.user.is_anonymous and request.user.is_authenticated: - votante = Votante.objects.get(user=request.user) - else: - raise ObjectDoesNotExist - except ObjectDoesNotExist: - logger.error(f"user={username}. Usuário (user={request.user}) não cadastrado como votante na tela de parlamentares. " - "Contate a administração de sua Casa Legislativa!") - msg = _("Usuário não cadastrado como votante na tela de parlamentares. Contate a administração de sua Casa Legislativa!") - context.update({'error_message': msg}) - - return render(request, template_name, context) - context_vars = {'votante': votante} + if not Votante.objects.filter(user=request.user).exists(): + logger.warning( + f'user={username} sem cadastro de Votante tentou acessar /voto-individual/.' + ) + raise PermissionDenied + + template_name = 'painel/voto_individual.html' context = {'head_title': str(_('Votação Individual'))} + context_vars = {'votante': Votante.objects.get(user=request.user)} - # Verifica se usuário possui permissão para votar - if 'parlamentares.can_vote' in request.user.get_all_permissions(): - context, context_vars = can_vote(context, context_vars, request) - logger.debug("user=" + username + ". Verificando se usuário {} possui permissão para votar.".format(request.user)) - else: - logger.error("user=" + username + ". Usuário {} sem permissão para votar.".format(request.user)) - context.update({'permissao': False, - 'error_message': 'Usuário sem permissão para votar.'}) + context, context_vars = can_vote(context, context_vars, request) # Salva o voto if request.method == 'POST': diff --git a/sapl/templates/painel/voto_individual.html b/sapl/templates/painel/voto_individual.html index c35daa5fa..1dde0990d 100644 --- a/sapl/templates/painel/voto_individual.html +++ b/sapl/templates/painel/voto_individual.html @@ -127,10 +127,11 @@ +{% if not error_message %} {% render_chunk_vendors 'js' %} -{% render_bundle 'global' 'js' %} -{% render_bundle 'painel' 'js' %} - +{% render_bundle 'global' 'js' %} +{% render_bundle 'painel' 'js' %} + +{% endif %}