Browse Source

Merge branch '3.1.x' into tipo_votacao_multiplas_materias

pull/3781/head
cristian-longhi 2 weeks ago
committed by GitHub
parent
commit
3661516253
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 9
      sapl/audiencia/views.py
  2. 8
      sapl/base/views.py
  3. 11
      sapl/comissoes/views.py
  4. 62
      sapl/crud/base.py
  5. 40
      sapl/materia/views.py
  6. 3
      sapl/middleware.py
  7. 20
      sapl/norma/views.py
  8. 19
      sapl/parlamentares/views.py
  9. 27
      sapl/protocoloadm/views.py
  10. 67
      sapl/relatorios/views.py
  11. 15
      sapl/sessao/views.py
  12. 2
      sapl/settings.py
  13. 5
      sapl/static/.well-known/traffic-advice
  14. 15
      sapl/urls.py
  15. 9
      scripts/test_ratelimiter.sh

9
sapl/audiencia/views.py

@ -9,6 +9,12 @@ from sapl.crud.base import RP_DETAIL, RP_LIST, Crud, MasterDetailCrud
from .forms import AudienciaForm, AnexoAudienciaPublicaForm
from .models import AudienciaPublica, AnexoAudienciaPublica
from ratelimit.decorators import ratelimit
from django.utils.decorators import method_decorator
from ..settings import RATE_LIMITER_RATE
from ..utils import ratelimit_ip
def index(request):
return HttpResponse("Audiência Pública")
@ -105,6 +111,3 @@ class AnexoAudienciaPublicaCrud(MasterDetailCrud):
qs = super(MasterDetailCrud.ListView, self).get_queryset()
kwargs = {self.crud.parent_field: self.kwargs['pk']}
return qs.filter(**kwargs).order_by('-data', '-id')
class DetailView(AudienciaPublicaMixin, MasterDetailCrud.DetailView):
pass

8
sapl/base/views.py

@ -48,7 +48,7 @@ from sapl.parlamentares.models import (
from sapl.protocoloadm.models import (Anexado, Protocolo)
from sapl.relatorios.views import (relatorio_estatisticas_acesso_normas)
from sapl.sessao.models import (Bancada, SessaoPlenaria)
from sapl.settings import EMAIL_SEND_USER
from sapl.settings import EMAIL_SEND_USER, RATE_LIMITER_RATE
from sapl.utils import (gerar_hash_arquivo, intervalos_tem_intersecao, mail_service_configured,
SEPARADOR_HASH_PROPOSICAO, show_results_filter_set, google_recaptcha_configured,
get_client_ip, sapn_is_enabled, is_weak_password, ratelimit_ip)
@ -68,7 +68,7 @@ class IndexView(TemplateView):
@method_decorator(ratelimit(key=ratelimit_ip,
rate='10/m',
rate=RATE_LIMITER_RATE,
method=ratelimit.UNSAFE,
block=True),
name='dispatch')
@ -1400,6 +1400,10 @@ class SaplSearchView(SearchView):
return context
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class PesquisarAuditLogView(PermissionRequiredMixin, FilterView):
model = AuditLog
filterset_class = AuditLogFilterSet

11
sapl/comissoes/views.py

@ -28,11 +28,16 @@ from sapl.crud.base import (Crud, CrudAux, MasterDetailCrud,
RP_LIST)
from sapl.materia.models import (MateriaEmTramitacao, MateriaLegislativa,
PautaReuniao, Tramitacao)
from sapl.utils import show_results_filter_set
from sapl.utils import show_results_filter_set, ratelimit_ip
from .models import (CargoComissao, Comissao, Composicao, DocumentoAcessorio,
Participacao, Periodo, Reuniao, TipoComissao)
from ratelimit.decorators import ratelimit
from django.utils.decorators import method_decorator
from ..settings import RATE_LIMITER_RATE
def pegar_url_composicao(pk):
participacao = Participacao.objects.get(id=pk)
@ -333,6 +338,10 @@ class RemovePautaView(PermissionRequiredMixin, CreateView):
return HttpResponseRedirect(success_url)
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class AdicionaPautaView(PermissionRequiredMixin, FilterView):
filterset_class = PautaReuniaoFilterSet
template_name = 'comissoes/pauta.html'

62
sapl/crud/base.py

@ -26,7 +26,11 @@ from sapl.crispy_layout_mixin import CrispyLayoutFormMixin, get_field_display
from sapl.crispy_layout_mixin import SaplFormHelper
from sapl.rules import (RP_ADD, RP_CHANGE, RP_DELETE, RP_DETAIL,
RP_LIST)
from sapl.utils import normalize
from sapl.settings import RATE_LIMITER_RATE
from sapl.utils import normalize, ratelimit_ip
from ratelimit.decorators import ratelimit
from django.utils.decorators import method_decorator
logger = logging.getLogger(settings.BASE_DIR.name)
@ -101,7 +105,6 @@ variáveis do crud:
class SearchMixin(models.Model):
search = models.TextField(blank=True, default='')
logger = logging.getLogger(__name__)
@ -238,7 +241,7 @@ class PermissionRequiredContainerCrudMixin(PermissionRequiredMixin):
@property
def container_field_set(self):
if hasattr(self, 'crud') and\
if hasattr(self, 'crud') and \
not hasattr(self.crud, 'container_field_set'):
self.crud.container_field_set = ''
if hasattr(self, 'crud'):
@ -267,7 +270,6 @@ class CrudBaseMixin(CrispyLayoutFormMixin):
obj.public = []
if hasattr(self, 'permission_required') and self.permission_required:
self.permission_required = tuple(
(
self.permission(pr) for pr in (
@ -337,7 +339,7 @@ class CrudBaseMixin(CrispyLayoutFormMixin):
if not obj.DetailView.permission_required:
return self.resolve_url(ACTION_DETAIL, args=(self.object.id,))
else:
return self.resolve_url(ACTION_DETAIL, args=(self.object.id,))\
return self.resolve_url(ACTION_DETAIL, args=(self.object.id,)) \
if self.request.user.has_perm(
self.permission(RP_DETAIL)) else ''
@ -347,7 +349,7 @@ class CrudBaseMixin(CrispyLayoutFormMixin):
if not obj.UpdateView.permission_required:
return self.resolve_url(ACTION_UPDATE, args=(self.object.id,))
else:
return self.resolve_url(ACTION_UPDATE, args=(self.object.id,))\
return self.resolve_url(ACTION_UPDATE, args=(self.object.id,)) \
if self.request.user.has_perm(
self.permission(RP_CHANGE)) else ''
@ -357,7 +359,7 @@ class CrudBaseMixin(CrispyLayoutFormMixin):
if not obj.DeleteView.permission_required:
return self.resolve_url(ACTION_DELETE, args=(self.object.id,))
else:
return self.resolve_url(ACTION_DELETE, args=(self.object.id,))\
return self.resolve_url(ACTION_DELETE, args=(self.object.id,)) \
if self.request.user.has_perm(
self.permission(RP_DELETE)) else ''
@ -388,6 +390,10 @@ class CrudBaseMixin(CrispyLayoutFormMixin):
return self.model._meta.verbose_name_plural
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class CrudListView(PermissionRequiredContainerCrudMixin, ListView):
permission_required = (RP_LIST,)
logger = logging.getLogger(__name__)
@ -516,7 +522,7 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView):
um formulário de pesquisa herdado ou o próprio ListWithSearchForm.
pode ser usado se o model relativo herdar de SearchMixin"""
if hasattr(self, 'form_search_class'):
q = str(self.request.GET.get('q'))\
q = str(self.request.GET.get('q')) \
if 'q' in self.request.GET else ''
o = self.request.GET['o'] if 'o' in self.request.GET else '1'
@ -612,7 +618,7 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView):
)
pass
if fm and hasattr(fm, 'related_model')\
if fm and hasattr(fm, 'related_model') \
and fm.related_model:
rmo = fm.related_model._meta.ordering
if rmo:
@ -710,7 +716,7 @@ class CrudCreateView(PermissionRequiredContainerCrudMixin,
'sem estar em um Container %s'
) % container_model._meta.verbose_name)
if hasattr(self, 'crud') and\
if hasattr(self, 'crud') and \
hasattr(self.crud, 'is_m2m') and self.crud.is_m2m:
setattr(
self.object, container[1], getattr(
@ -724,9 +730,12 @@ class CrudCreateView(PermissionRequiredContainerCrudMixin,
return super().form_valid(form)
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class CrudDetailView(PermissionRequiredContainerCrudMixin,
DetailView, MultipleObjectMixin):
permission_required = (RP_DETAIL,)
no_entries_msg = _('Nenhum registro Associado.')
paginate_by = 10
@ -976,9 +985,8 @@ class Crud:
view.permission_required and \
hasattr(cls, 'public') and \
cls.public:
#print(view.permission_required, view)
#print(cls.public, cls)
# print(view.permission_required, view)
# print(cls.public, cls)
pr = pr - set(cls.public)
@ -1036,7 +1044,6 @@ class Crud:
def build(cls, _model, _help_topic, _model_set=None, list_field_names=[]):
def create_class(_list_field_names):
class ModelCrud(cls):
model = _model
model_set = _model_set
@ -1080,7 +1087,6 @@ class CrudAux(Crud):
@classonlymethod
def build(cls, _model, _help_topic, _model_set=None, list_field_names=[]):
ModelCrud = Crud.build(
_model, _help_topic, _model_set, list_field_names)
@ -1101,7 +1107,7 @@ class MasterDetailCrud(Crud):
obj = self.crud if hasattr(self, 'crud') else self
if not obj.ListView:
return ''
return self.resolve_url(ACTION_LIST, args=(self.kwargs['pk'],))\
return self.resolve_url(ACTION_LIST, args=(self.kwargs['pk'],)) \
if self.request.user.has_perm(self.permission(RP_LIST)) else ''
@property
@ -1109,7 +1115,7 @@ class MasterDetailCrud(Crud):
obj = self.crud if hasattr(self, 'crud') else self
if not obj.CreateView:
return ''
return self.resolve_url(ACTION_CREATE, args=(self.kwargs['pk'],))\
return self.resolve_url(ACTION_CREATE, args=(self.kwargs['pk'],)) \
if self.request.user.has_perm(self.permission(RP_ADD)) else ''
@property
@ -1118,7 +1124,7 @@ class MasterDetailCrud(Crud):
if not obj.DetailView:
return ''
pkk = self.request.GET['pkk'] if 'pkk' in self.request.GET else ''
return (super().detail_url + (('?pkk=' + pkk) if pkk else ''))\
return (super().detail_url + (('?pkk=' + pkk) if pkk else '')) \
if self.request.user.has_perm(
self.permission(RP_DETAIL)) else ''
@ -1128,7 +1134,7 @@ class MasterDetailCrud(Crud):
if not obj.UpdateView:
return ''
pkk = self.request.GET['pkk'] if 'pkk' in self.request.GET else ''
return (super().update_url + (('?pkk=' + pkk) if pkk else ''))\
return (super().update_url + (('?pkk=' + pkk) if pkk else '')) \
if self.request.user.has_perm(
self.permission(RP_CHANGE)) else ''
@ -1137,7 +1143,7 @@ class MasterDetailCrud(Crud):
obj = self.crud if hasattr(self, 'crud') else self
if not obj.DeleteView:
return ''
return super().delete_url\
return super().delete_url \
if self.request.user.has_perm(
self.permission(RP_DELETE)) else ''
@ -1168,7 +1174,7 @@ class MasterDetailCrud(Crud):
root_pk = parent_object.pk
else:
root_pk = self.kwargs['pk'] if 'pkk' not in self.request.GET\
root_pk = self.kwargs['pk'] if 'pkk' not in self.request.GET \
else self.request.GET['pkk']
kwargs.setdefault('root_pk', root_pk)
@ -1182,6 +1188,10 @@ class MasterDetailCrud(Crud):
context['title'] = title
return context
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class ListView(Crud.ListView):
permission_required = RP_LIST,
logger = logging.getLogger(__name__)
@ -1414,6 +1424,10 @@ class MasterDetailCrud(Crud):
else:
return self.resolve_url(ACTION_LIST, args=(pk,))
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class DetailView(Crud.DetailView):
permission_required = RP_DETAIL,
template_name = 'crud/detail_detail.html'
@ -1429,7 +1443,7 @@ class MasterDetailCrud(Crud):
if not obj.ListView:
return ''
if obj.ListView.permission_required not in obj.public or\
if obj.ListView.permission_required not in obj.public or \
self.request.user.has_perm(self.permission(RP_LIST)):
if '__' in obj.parent_field:
fields = obj.parent_field.split('__')
@ -1499,7 +1513,7 @@ class MasterDetailCrud(Crud):
@property
def detail_set_create_url(self):
obj = self.crud if hasattr(self, 'crud') else self
if hasattr(obj, 'model_set') and obj.model_set\
if hasattr(obj, 'model_set') and obj.model_set \
and self.request.user.has_perm(
self.permission_set(RP_ADD)):
root_pk = self.object.pk

40
sapl/materia/views.py

@ -51,7 +51,7 @@ from sapl.materia.forms import (AnexadaForm, AutoriaForm, AutoriaMultiCreateForm
from sapl.norma.models import LegislacaoCitada
from sapl.parlamentares.models import Legislatura
from sapl.protocoloadm.models import Protocolo
from sapl.settings import MAX_DOC_UPLOAD_SIZE, MEDIA_ROOT
from sapl.settings import MAX_DOC_UPLOAD_SIZE, MEDIA_ROOT, RATE_LIMITER_RATE
from sapl.utils import (autor_label, autor_modal, gerar_hash_arquivo, get_base_url,
get_client_ip, get_mime_type_from_file_extension, lista_anexados,
mail_service_configured, montar_row_autor, SEPARADOR_HASH_PROPOSICAO,
@ -134,6 +134,10 @@ def proposicao_texto(request, pk):
raise Http404
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class AdicionarVariasAutorias(PermissionRequiredForAppCrudMixin, FilterView):
app_label = sapl.materia.apps.AppConfig.label
filterset_class = AdicionarVariasAutoriasFilterSet
@ -394,6 +398,10 @@ class StatusTramitacaoCrud(CrudAux):
return reverse('sapl.materia:pesquisar_statustramitacao')
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class PesquisarStatusTramitacaoView(FilterView):
model = StatusTramitacao
filterset_class = StatusTramitacaoFilterSet
@ -1461,10 +1469,6 @@ class TramitacaoCrud(MasterDetailCrud):
return initial
@method_decorator(ratelimit(key=ratelimit_ip,
rate='10/m',
block=True),
name='dispatch')
class ListView(MasterDetailCrud.ListView):
def get_queryset(self):
@ -1537,10 +1541,6 @@ class TramitacaoCrud(MasterDetailCrud):
return HttpResponseRedirect(url)
@method_decorator(ratelimit(key=ratelimit_ip,
rate='10/m',
block=True),
name='dispatch')
class DetailView(MasterDetailCrud.DetailView):
template_name = "materia/tramitacao_detail.html"
@ -1918,10 +1918,6 @@ class MateriaLegislativaCrud(Crud):
def get_success_url(self):
return self.search_url
@method_decorator(ratelimit(key=ratelimit_ip,
rate='10/m',
block=True),
name='dispatch')
class DetailView(Crud.DetailView):
layout_key = 'MateriaLegislativaDetail'
@ -1934,10 +1930,6 @@ class MateriaLegislativaCrud(Crud):
pk=self.kwargs['pk'])
return context
@method_decorator(ratelimit(key=ratelimit_ip,
rate='10/m',
block=True),
name='dispatch')
class ListView(Crud.ListView, RedirectView):
def get_redirect_url(self, *args, **kwargs):
@ -2059,7 +2051,7 @@ class AcompanhamentoExcluirView(TemplateView):
@method_decorator(ratelimit(key=ratelimit_ip,
rate='10/m',
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class MateriaLegislativaPesquisaView(MultiFormatOutputMixin, FilterView):
@ -2323,6 +2315,10 @@ class AcompanhamentoMateriaView(CreateView):
kwargs={'pk': self.kwargs['pk']})
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class DocumentoAcessorioEmLoteView(PermissionRequiredMixin, FilterView):
filterset_class = AcessorioEmLoteFilterSet
template_name = 'materia/em_lote/acessorio.html'
@ -2435,6 +2431,10 @@ class DocumentoAcessorioEmLoteView(PermissionRequiredMixin, FilterView):
return self.get(request, self.kwargs)
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class MateriaAnexadaEmLoteView(PermissionRequiredMixin, FilterView):
filterset_class = AnexadaEmLoteFilterSet
template_name = 'materia/em_lote/anexada.html'
@ -2559,6 +2559,10 @@ class MateriaAnexadaEmLoteView(PermissionRequiredMixin, FilterView):
return HttpResponseRedirect(success_url)
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView):
filterset_class = PrimeiraTramitacaoEmLoteFilterSet
template_name = 'materia/em_lote/tramitacao.html'

3
sapl/middleware.py

@ -19,3 +19,6 @@ class CheckWeakPasswordMiddleware:
return redirect('sapl.base:alterar_senha')
return self.get_response(request)

20
sapl/norma/views.py

@ -38,7 +38,7 @@ from .forms import (AnexoNormaJuridicaForm, NormaFilterSet, NormaJuridicaForm,
AutoriaNormaForm, AssuntoNormaFilterSet)
from .models import (AnexoNormaJuridica, AssuntoNorma, NormaJuridica, NormaRelacionada,
TipoNormaJuridica, TipoVinculoNormaJuridica, AutoriaNorma, NormaEstatisticas)
from ..settings import RATE_LIMITER_RATE
# LegislacaoCitadaCrud = Crud.build(LegislacaoCitada, '')
TipoNormaCrud = CrudAux.build(
@ -60,6 +60,10 @@ class AssuntoNormaCrud(CrudAux):
return reverse('sapl.norma:pesquisar_assuntonorma')
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class PesquisarAssuntoNormaView(FilterView):
model = AssuntoNorma
filterset_class = AssuntoNormaFilterSet
@ -151,7 +155,7 @@ class NormaRelacionadaCrud(MasterDetailCrud):
@method_decorator(ratelimit(key=ratelimit_ip,
rate='10/m',
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class NormaPesquisaView(MultiFormatOutputMixin, FilterView):
@ -239,10 +243,6 @@ class AnexoNormaJuridicaCrud(MasterDetailCrud):
initial['ano'] = self.object.ano
return initial
@method_decorator(ratelimit(key=ratelimit_ip,
rate='10/m',
block=True),
name='dispatch')
class DetailView(MasterDetailCrud.DetailView):
form_class = AnexoNormaJuridicaForm
layout_key = 'AnexoNormaJuridica'
@ -291,10 +291,6 @@ class NormaCrud(Crud):
namespace = self.model._meta.app_config.name
return reverse('%s:%s' % (namespace, 'norma_pesquisa'))
@method_decorator(ratelimit(key=ratelimit_ip,
rate='10/m',
block=True),
name='dispatch')
class DetailView(Crud.DetailView):
def get(self, request, *args, **kwargs):
estatisticas_acesso_normas = AppConfig.objects.first().estatisticas_acesso_normas
@ -352,10 +348,6 @@ class NormaCrud(Crud):
layout_key = 'NormaJuridicaCreate'
@method_decorator(ratelimit(key=ratelimit_ip,
rate='10/m',
block=True),
name='dispatch')
class ListView(Crud.ListView):
def get(self, request, *args, **kwargs):

19
sapl/parlamentares/views.py

@ -33,7 +33,7 @@ from sapl.materia.models import Autoria, Proposicao, Relatoria
from sapl.norma.models import AutoriaNorma, NormaJuridica
from sapl.parlamentares.apps import AppConfig
from sapl.rules import SAPL_GROUP_VOTANTE
from sapl.utils import (parlamentares_ativos, show_results_filter_set)
from sapl.utils import (parlamentares_ativos, show_results_filter_set, ratelimit_ip)
from .forms import (ColigacaoFilterSet, FiliacaoForm, FrenteForm, LegislaturaForm, MandatoForm,
ParlamentarCreateForm, ParlamentarForm, VotanteForm,
@ -45,6 +45,11 @@ from .models import (CargoMesa, Coligacao, ComposicaoColigacao, ComposicaoMesa,
SituacaoMilitar, TipoAfastamento, TipoDependente, Votante,
Bloco, FrenteCargo, FrenteParlamentar, BlocoCargo, BlocoMembro, MesaDiretora)
from ratelimit.decorators import ratelimit
from django.utils.decorators import method_decorator
from ..settings import RATE_LIMITER_RATE
FrenteCargoCrud = CrudAux.build(FrenteCargo, 'frente_cargo')
BlocoCargoCrud = CrudAux.build(BlocoCargo, 'bloco_cargo')
CargoMesaCrud = CrudAux.build(CargoMesa, 'cargo_mesa')
@ -183,6 +188,10 @@ class ProposicaoParlamentarCrud(CrudBaseForListAndDetailExternalAppView):
_('Texto Eletrônico'))
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class PesquisarParlamentarView(FilterView):
model = Parlamentar
filterset_class = ParlamentarFilterSet
@ -245,6 +254,10 @@ class PesquisarParlamentarView(FilterView):
return self.render_to_response(context)
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class PesquisarColigacaoView(FilterView):
model = Coligacao
filterset_class = ColigacaoFilterSet
@ -301,6 +314,10 @@ class PesquisarColigacaoView(FilterView):
return self.render_to_response(context)
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class PesquisarPartidoView(FilterView):
model = Partido
filterset_class = PartidoFilterSet

27
sapl/protocoloadm/views.py

@ -47,7 +47,7 @@ from sapl.relatorios.views import relatorio_doc_administrativos
from sapl.utils import (create_barcode, get_base_url, get_client_ip,
get_mime_type_from_file_extension, lista_anexados,
show_results_filter_set, mail_service_configured, from_date_to_datetime_utc,
google_recaptcha_configured, get_tempfile_dir, MultiFormatOutputMixin)
google_recaptcha_configured, get_tempfile_dir, MultiFormatOutputMixin, ratelimit_ip)
from .forms import (AcompanhamentoDocumentoForm, AnexadoEmLoteFilterSet, AnexadoForm,
AnularProtocoloAdmForm, compara_tramitacoes_doc,
@ -62,7 +62,10 @@ from .forms import (AcompanhamentoDocumentoForm, AnexadoEmLoteFilterSet, Anexado
from .models import (Anexado, AcompanhamentoDocumento, DocumentoAcessorioAdministrativo,
DocumentoAdministrativo, StatusTramitacaoAdministrativo,
TipoDocumentoAdministrativo, TramitacaoAdministrativo)
from ..settings import MEDIA_ROOT
from ..settings import MEDIA_ROOT, RATE_LIMITER_RATE
from ratelimit.decorators import ratelimit
from django.utils.decorators import method_decorator
TipoDocumentoAdministrativoCrud = CrudAux.build(
TipoDocumentoAdministrativo, '')
@ -535,6 +538,10 @@ class StatusTramitacaoAdministrativoCrud(CrudAux):
ordering = 'sigla'
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class ProtocoloPesquisaView(PermissionRequiredMixin, FilterView):
model = Protocolo
filterset_class = ProtocoloFilterSet
@ -1032,6 +1039,10 @@ class ProtocoloMateriaTemplateView(PermissionRequiredMixin, TemplateView):
return context
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class PesquisarDocumentoAdministrativoView(DocumentoAdministrativoMixin,
MultiFormatOutputMixin,
PermissionRequiredMixin,
@ -1165,6 +1176,10 @@ class AnexadoCrud(MasterDetailCrud):
return 'AnexadoDetail'
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class DocumentoAnexadoEmLoteView(PermissionRequiredMixin, FilterView):
filterset_class = AnexadoEmLoteFilterSet
template_name = 'protocoloadm/em_lote/anexado.html'
@ -1642,6 +1657,10 @@ class FichaSelecionaAdmView(PermissionRequiredMixin, FormView):
'materia/impressos/ficha_adm_pdf.html')
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class PrimeiraTramitacaoEmLoteAdmView(PermissionRequiredMixin, FilterView):
filterset_class = PrimeiraTramitacaoEmLoteAdmFilterSet
template_name = 'protocoloadm/em_lote/tramitacaoadm.html'
@ -1878,6 +1897,10 @@ class VinculoDocAdminMateriaCrud(MasterDetailCrud):
return context
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class VinculoDocAdminMateriaEmLoteView(PermissionRequiredMixin, FilterView):
filterset_class = VinculoDocAdminMateriaEmLoteFilterSet
template_name = 'protocoloadm/em_lote/vinculodocadminmateria.html'

67
sapl/relatorios/views.py

@ -48,10 +48,10 @@ from sapl.sessao.views import (get_identificacao_basica, get_mesa_diretora,
get_oradores_explicacoes_pessoais, get_consideracoes_finais,
get_ocorrencias_da_sessao, get_assinaturas,
get_correspondencias)
from sapl.settings import MEDIA_URL
from sapl.settings import MEDIA_URL, RATE_LIMITER_RATE
from sapl.settings import STATIC_ROOT
from sapl.utils import LISTA_DE_UFS, TrocaTag, filiacao_data, create_barcode, show_results_filter_set, \
num_materias_por_tipo, parlamentares_ativos, MultiFormatOutputMixin
num_materias_por_tipo, parlamentares_ativos, MultiFormatOutputMixin, ratelimit_ip
from .templates import (pdf_capa_processo_gerar,
pdf_documento_administrativo_gerar, pdf_espelho_gerar,
pdf_etiqueta_protocolo_gerar, pdf_materia_gerar,
@ -59,6 +59,9 @@ from .templates import (pdf_capa_processo_gerar,
pdf_protocolo_gerar, pdf_sessao_plenaria_gerar)
from sapl.crud.base import make_pagination
from ratelimit.decorators import ratelimit
from django.utils.decorators import method_decorator
def get_kwargs_params(request, fields):
kwargs = {}
@ -1841,6 +1844,10 @@ class RelatorioMixin:
return self.render_to_response(context)
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class RelatorioDocumentosAcessoriosView(RelatorioMixin, FilterView):
model = DocumentoAcessorio
filterset_class = RelatorioDocumentosAcessoriosFilterSet
@ -1885,6 +1892,10 @@ class RelatorioDocumentosAcessoriosView(RelatorioMixin, FilterView):
return context
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class RelatorioVotacoesNominaisView(RelatorioMixin, MultiFormatOutputMixin, FilterView):
model = VotoParlamentar
filterset_class = RelatorioVotacoesNominaisFilterSet
@ -1954,6 +1965,10 @@ class RelatorioVotacoesNominaisView(RelatorioMixin, MultiFormatOutputMixin, Filt
return context
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class RelatorioAtasView(RelatorioMixin, FilterView):
model = SessaoPlenaria
filterset_class = RelatorioAtasFilterSet
@ -1979,6 +1994,10 @@ class RelatorioAtasView(RelatorioMixin, FilterView):
return context
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class RelatorioPresencaSessaoView(RelatorioMixin, FilterView):
logger = logging.getLogger(__name__)
model = SessaoPlenaria
@ -2213,6 +2232,10 @@ class RelatorioPresencaSessaoView(RelatorioMixin, FilterView):
return context
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class RelatorioHistoricoTramitacaoView(RelatorioMixin, FilterView):
model = MateriaLegislativa
filterset_class = RelatorioHistoricoTramitacaoFilterSet
@ -2270,6 +2293,10 @@ class RelatorioHistoricoTramitacaoView(RelatorioMixin, FilterView):
return context
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class RelatorioDataFimPrazoTramitacaoView(RelatorioMixin, FilterView):
model = MateriaEmTramitacao
filterset_class = RelatorioDataFimPrazoTramitacaoFilterSet
@ -2333,6 +2360,10 @@ class RelatorioDataFimPrazoTramitacaoView(RelatorioMixin, FilterView):
return context
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class RelatorioReuniaoView(RelatorioMixin, FilterView):
model = Reuniao
filterset_class = RelatorioReuniaoFilterSet
@ -2367,6 +2398,10 @@ class RelatorioReuniaoView(RelatorioMixin, FilterView):
return context
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class RelatorioAudienciaView(RelatorioMixin, FilterView):
model = AudienciaPublica
filterset_class = RelatorioAudienciaFilterSet
@ -2401,6 +2436,10 @@ class RelatorioAudienciaView(RelatorioMixin, FilterView):
return context
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class RelatorioMateriasTramitacaoView(RelatorioMixin, FilterView):
model = MateriaEmTramitacao
filterset_class = RelatorioMateriasTramitacaoFilterSet
@ -2515,6 +2554,10 @@ class RelatorioMateriasTramitacaoView(RelatorioMixin, FilterView):
return context
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class RelatorioMateriasPorAnoAutorTipoView(RelatorioMixin, FilterView):
model = MateriaLegislativa
filterset_class = RelatorioMateriasPorAnoAutorTipoFilterSet
@ -2594,6 +2637,10 @@ class RelatorioMateriasPorAnoAutorTipoView(RelatorioMixin, FilterView):
return context
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class RelatorioMateriasPorAutorView(RelatorioMixin, FilterView):
model = MateriaLegislativa
filterset_class = RelatorioMateriasPorAutorFilterSet
@ -2665,6 +2712,10 @@ class RelatorioMateriaAnoAssuntoView(ListView):
return context
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class RelatorioNormasPublicadasMesView(RelatorioMixin, FilterView):
model = NormaJuridica
filterset_class = RelatorioNormasMesFilterSet
@ -2705,6 +2756,10 @@ class RelatorioNormasPublicadasMesView(RelatorioMixin, FilterView):
return context
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class RelatorioNormasVigenciaView(RelatorioMixin, FilterView):
model = NormaJuridica
filterset_class = RelatorioNormasVigenciaFilterSet
@ -2769,6 +2824,10 @@ class RelatorioNormasVigenciaView(RelatorioMixin, FilterView):
return context
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class RelatorioHistoricoTramitacaoAdmView(RelatorioMixin, FilterView):
model = DocumentoAdministrativo
filterset_class = RelatorioHistoricoTramitacaoAdmFilterSet
@ -2819,6 +2878,10 @@ class RelatorioHistoricoTramitacaoAdmView(RelatorioMixin, FilterView):
return context
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class RelatorioNormasPorAutorView(RelatorioMixin, FilterView):
model = NormaJuridica
filterset_class = RelatorioNormasPorAutorFilterSet

15
sapl/sessao/views.py

@ -47,7 +47,7 @@ from sapl.sessao.apps import AppConfig
from sapl.sessao.forms import ExpedienteMateriaForm, OrdemDiaForm, OrdemExpedienteLeituraForm, \
CorrespondenciaForm, CorrespondenciaEmLoteFilterSet
from sapl.sessao.models import Correspondencia
from sapl.settings import TIME_ZONE
from sapl.settings import TIME_ZONE, RATE_LIMITER_RATE
from sapl.utils import show_results_filter_set, remover_acentos, get_client_ip, \
MultiFormatOutputMixin, PautaMultiFormatOutputMixin, ratelimit_ip
@ -3798,7 +3798,7 @@ class SessaoListView(ListView):
@method_decorator(ratelimit(key=ratelimit_ip,
rate='10/m',
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class PautaSessaoView(TemplateView):
@ -3817,7 +3817,7 @@ class PautaSessaoView(TemplateView):
@method_decorator(ratelimit(key=ratelimit_ip,
rate='10/m',
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class PautaSessaoDetailView(PautaMultiFormatOutputMixin, DetailView):
@ -4001,6 +4001,10 @@ class PautaSessaoDetailView(PautaMultiFormatOutputMixin, DetailView):
return self.render_to_response(context)
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class PesquisarSessaoPlenariaView(MultiFormatOutputMixin, FilterView):
model = SessaoPlenaria
filterset_class = SessaoPlenariaFilterSet
@ -4088,6 +4092,7 @@ class PesquisarSessaoPlenariaView(MultiFormatOutputMixin, FilterView):
return r
class PesquisarPautaSessaoView(PesquisarSessaoPlenariaView):
filterset_class = PautaSessaoFilterSet
template_name = 'sessao/pauta_sessao_filter.html'
@ -5388,6 +5393,10 @@ class CorrespondenciaCrud(MasterDetailCrud):
return obj
@method_decorator(ratelimit(key=ratelimit_ip,
rate=RATE_LIMITER_RATE,
block=True),
name='dispatch')
class CorrespondenciaEmLoteView(PermissionRequiredMixin, FilterView):
filterset_class = CorrespondenciaEmLoteFilterSet
template_name = 'sessao/em_lote/correspondencia.html'

2
sapl/settings.py

@ -315,6 +315,8 @@ MAX_DOC_UPLOAD_SIZE = 150 * 1024 * 1024 # 150MB
MAX_IMAGE_UPLOAD_SIZE = 2 * 1024 * 1024 # 2MB
DATA_UPLOAD_MAX_MEMORY_SIZE = 10 * 1024 * 1024 # 10MB
RATE_LIMITER_RATE = config('RATE_LIMITER_RATE', default='10/m')
# Internationalization
# https://docs.djangoproject.com/en/1.8/topics/i18n/
LANGUAGE_CODE = 'pt-br'

5
sapl/static/.well-known/traffic-advice

@ -0,0 +1,5 @@
{
"interval": 300,
"requests-per-second": 5,
"burst": 10
}

15
sapl/urls.py

@ -100,3 +100,18 @@ if settings.DEBUG:
'document_root': settings.MEDIA_ROOT,
}),
]
# Make the rate limiter return 429 (Too Many Requests) instead of 403 (Forbidden Access)
def custom_permission_denied_view(request, exception=None):
from django.http import HttpResponse, HttpResponseForbidden
from ratelimit.exceptions import Ratelimited
if isinstance(exception, Ratelimited):
resp = HttpResponse('Too many requests', status=429)
resp['Retry-After'] = '60'
return resp
return HttpResponseForbidden('Forbidden')
handler403 = custom_permission_denied_view

9
scripts/test_ratelimiter.sh

@ -1,9 +1,14 @@
#!/bin/bash
#URL=http://localhost:8000/materia/4379
URL=http://localhost:8000/norma/pesquisar
#URL=http://localhost:8000/norma/pesquisar
#URL=http://localhost/norma/pesquisar
#URL=https://sapl31demo.interlegis.leg.br/docadm/45
#URL=https://sapl.joaopessoa.pb.leg.br/materia/186300
#URL=http://localhost:8000/materia/4379/materiaassunto
#URL=http://localhost:8000/sessao/4984
URL="http://localhost:8000/docadm/pesq-doc-adm?tipo=&o=&numero=&complemento=&ano=&protocolo__numero=&numero_externo=&data_0=&data_1=&interessado=&assunto=&tramitacao=&tramitacaoadministrativo__status=&tramitacaoadministrativo__unidade_tramitacao_destino=&pesquisar=Pesquisar"
for i in $(seq 1 6); do
for i in $(seq 1 12); do
curl -sS -o /dev/null -w "req=$i http=%{http_code} time=%{time_total}\n" "$URL"
done

Loading…
Cancel
Save