Browse Source

Fix recibo proposição e adiciona rate limiter em matéria e norma

pull/3800/merge
Edward Ribeiro 1 month ago
parent
commit
3faba84bc8
  1. 7
      .dockerignore
  2. 2
      docker/config/nginx/sapl.conf
  3. 3
      docker/startup_scripts/gunicorn.conf.py
  4. 5
      docker/startup_scripts/start.sh
  5. 3
      requirements/requirements.txt
  6. 2
      sapl/base/views.py
  7. 9
      sapl/materia/views.py
  8. 7
      sapl/norma/views.py
  9. 5
      sapl/sessao/views.py
  10. 28
      sapl/utils.py
  11. 4
      scripts/gunicorn_start.sh

7
.dockerignore

@ -12,3 +12,10 @@ bower
.travis.yml .travis.yml
.env .env
.idea .idea
.DS_Store
.coveragerc
*.swp
.coveragerc
.drone.yml
.github
release.sh

2
docker/config/nginx/sapl.conf

@ -7,7 +7,7 @@ upstream sapl_server {
server { server {
listen 80; listen 80;
server_name sapl.test; server_name sapl.prod;
client_max_body_size 4G; client_max_body_size 4G;

3
docker/startup_scripts/gunicorn.conf.py

@ -52,7 +52,8 @@ graceful_timeout = 30
keepalive = 10 keepalive = 10
backlog = 2048 backlog = 2048
max_requests = MAX_REQUESTS max_requests = MAX_REQUESTS
max_requests_jitter = 100 max_requests_jitter = 200
worker_max_memory_per_child = 300 * 1024 * 1024 # 300 MB cap
# Environment (same as exporting before running) # Environment (same as exporting before running)
raw_env = [ raw_env = [

5
docker/startup_scripts/start.sh

@ -4,19 +4,16 @@ IFS=$'\n\t'
APP_DIR="/var/interlegis/sapl" APP_DIR="/var/interlegis/sapl"
DATA_DIR="/var/interlegis/sapl/data" DATA_DIR="/var/interlegis/sapl/data"
MEDIA_DIR="/var/interlegis/sapl/media"
RUN_DIR="/var/interlegis/sapl/run" RUN_DIR="/var/interlegis/sapl/run"
ENV_FILE="$APP_DIR/.env" ENV_FILE="$APP_DIR/.env"
SECRET_FILE="$DATA_DIR/secret.key" SECRET_FILE="$DATA_DIR/secret.key"
chown -R root:nginx "$RUN_DIR" || true chown -R root:nginx "$RUN_DIR" || true
chown -R root:nginx "$MEDIA_DIR" || true
chmod -R g+rwX "$RUN_DIR" || true chmod -R g+rwX "$RUN_DIR" || true
chmod -R g+rwX "$MEDIA_DIR" || true
# setgid bit on our writable trees (not data/) # setgid bit on our writable trees (not data/)
find "$RUN_DIR" "$MEDIA_DIR" -type d -exec chmod g+s {} + 2>/dev/null || true find "$RUN_DIR" -type d -exec chmod g+s {} + 2>/dev/null || true
log() { printf '[%s] %s\n' "$(date -Is)" "$*"; } log() { printf '[%s] %s\n' "$(date -Is)" "$*"; }
err() { printf '[%s] ERROR: %s\n' "$(date -Is)" "$*" >&2; } err() { printf '[%s] ERROR: %s\n' "$(date -Is)" "$*" >&2; }

3
requirements/requirements.txt

@ -22,8 +22,7 @@ pytz==2019.3
python-magic==0.4.15 python-magic==0.4.15
unipath==1.1 unipath==1.1
Pillow==10.3.0 Pillow==10.3.0
rlPyCairo==0.3.0 reportlab==3.6.13
reportlab==4.2.0
WeasyPrint==66 WeasyPrint==66
trml2pdf==0.6 trml2pdf==0.6
gunicorn==23.0.0 gunicorn==23.0.0

2
sapl/base/views.py

@ -68,7 +68,7 @@ class IndexView(TemplateView):
@method_decorator(ratelimit(key=lambda group, request: get_client_ip(request), @method_decorator(ratelimit(key=lambda group, request: get_client_ip(request),
rate='20/m', rate='10/m',
method=ratelimit.UNSAFE, method=ratelimit.UNSAFE,
block=True), name='dispatch') block=True), name='dispatch')
class LoginSapl(views.LoginView): class LoginSapl(views.LoginView):

9
sapl/materia/views.py

@ -24,7 +24,6 @@ from django.shortcuts import render
from django.template import loader from django.template import loader
from django.urls import reverse from django.urls import reverse
from django.utils import formats, timezone from django.utils import formats, timezone
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.views.generic import CreateView, ListView, TemplateView, UpdateView from django.views.generic import CreateView, ListView, TemplateView, UpdateView
from django.views.generic.base import RedirectView from django.views.generic.base import RedirectView
@ -32,6 +31,9 @@ from django.views.generic.edit import FormView
from django_filters.views import FilterView from django_filters.views import FilterView
import weasyprint import weasyprint
from ratelimit.decorators import ratelimit
from django.utils.decorators import method_decorator
import sapl import sapl
from sapl.base.email_utils import do_envia_email_confirmacao from sapl.base.email_utils import do_envia_email_confirmacao
from sapl.base.models import Autor, CasaLegislativa, AppConfig as BaseAppConfig from sapl.base.models import Autor, CasaLegislativa, AppConfig as BaseAppConfig
@ -1459,6 +1461,7 @@ class TramitacaoCrud(MasterDetailCrud):
return initial return initial
@method_decorator(ratelimit(key='ip', rate='10/m', block=True), name='dispatch')
class ListView(MasterDetailCrud.ListView): class ListView(MasterDetailCrud.ListView):
def get_queryset(self): def get_queryset(self):
@ -1531,6 +1534,7 @@ class TramitacaoCrud(MasterDetailCrud):
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
@method_decorator(ratelimit(key='ip', rate='10/m', block=True), name='dispatch')
class DetailView(MasterDetailCrud.DetailView): class DetailView(MasterDetailCrud.DetailView):
template_name = "materia/tramitacao_detail.html" template_name = "materia/tramitacao_detail.html"
@ -1908,6 +1912,7 @@ class MateriaLegislativaCrud(Crud):
def get_success_url(self): def get_success_url(self):
return self.search_url return self.search_url
@method_decorator(ratelimit(key='ip', rate='10/m', block=True), name='dispatch')
class DetailView(Crud.DetailView): class DetailView(Crud.DetailView):
layout_key = 'MateriaLegislativaDetail' layout_key = 'MateriaLegislativaDetail'
@ -1920,6 +1925,7 @@ class MateriaLegislativaCrud(Crud):
pk=self.kwargs['pk']) pk=self.kwargs['pk'])
return context return context
@method_decorator(ratelimit(key='ip', rate='10/m', block=True), name='dispatch')
class ListView(Crud.ListView, RedirectView): class ListView(Crud.ListView, RedirectView):
def get_redirect_url(self, *args, **kwargs): def get_redirect_url(self, *args, **kwargs):
@ -2040,6 +2046,7 @@ class AcompanhamentoExcluirView(TemplateView):
return HttpResponseRedirect(self.get_success_url()) return HttpResponseRedirect(self.get_success_url())
@method_decorator(ratelimit(key='ip', rate='10/m', block=True), name='dispatch')
class MateriaLegislativaPesquisaView(MultiFormatOutputMixin, FilterView): class MateriaLegislativaPesquisaView(MultiFormatOutputMixin, FilterView):
model = MateriaLegislativa model = MateriaLegislativa
filterset_class = MateriaLegislativaFilterSet filterset_class = MateriaLegislativaFilterSet

7
sapl/norma/views.py

@ -19,6 +19,9 @@ from django.views.generic.edit import FormView
from django_filters.views import FilterView from django_filters.views import FilterView
import weasyprint import weasyprint
from ratelimit.decorators import ratelimit
from django.utils.decorators import method_decorator
from sapl import settings from sapl import settings
import sapl import sapl
from sapl.base.models import AppConfig from sapl.base.models import AppConfig
@ -147,6 +150,7 @@ class NormaRelacionadaCrud(MasterDetailCrud):
layout_key = 'NormaRelacionadaDetail' layout_key = 'NormaRelacionadaDetail'
@method_decorator(ratelimit(key='ip', rate='10/m', block=True), name='dispatch')
class NormaPesquisaView(MultiFormatOutputMixin, FilterView): class NormaPesquisaView(MultiFormatOutputMixin, FilterView):
model = NormaJuridica model = NormaJuridica
filterset_class = NormaFilterSet filterset_class = NormaFilterSet
@ -232,6 +236,7 @@ class AnexoNormaJuridicaCrud(MasterDetailCrud):
initial['ano'] = self.object.ano initial['ano'] = self.object.ano
return initial return initial
@method_decorator(ratelimit(key='ip', rate='10/m', block=True), name='dispatch')
class DetailView(MasterDetailCrud.DetailView): class DetailView(MasterDetailCrud.DetailView):
form_class = AnexoNormaJuridicaForm form_class = AnexoNormaJuridicaForm
layout_key = 'AnexoNormaJuridica' layout_key = 'AnexoNormaJuridica'
@ -280,6 +285,7 @@ class NormaCrud(Crud):
namespace = self.model._meta.app_config.name namespace = self.model._meta.app_config.name
return reverse('%s:%s' % (namespace, 'norma_pesquisa')) return reverse('%s:%s' % (namespace, 'norma_pesquisa'))
@method_decorator(ratelimit(key='ip', rate='10/m', block=True), name='dispatch')
class DetailView(Crud.DetailView): class DetailView(Crud.DetailView):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
estatisticas_acesso_normas = AppConfig.objects.first().estatisticas_acesso_normas estatisticas_acesso_normas = AppConfig.objects.first().estatisticas_acesso_normas
@ -337,6 +343,7 @@ class NormaCrud(Crud):
layout_key = 'NormaJuridicaCreate' layout_key = 'NormaJuridicaCreate'
@method_decorator(ratelimit(key='ip', rate='10/m', block=True), name='dispatch')
class ListView(Crud.ListView): class ListView(Crud.ListView):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):

5
sapl/sessao/views.py

@ -28,6 +28,9 @@ from django.views.generic.edit import FormMixin
from django_filters.views import FilterView from django_filters.views import FilterView
import pytz import pytz
from ratelimit.decorators import ratelimit
from django.utils.decorators import method_decorator
from sapl.base.models import AppConfig as AppsAppConfig from sapl.base.models import AppConfig as AppsAppConfig
from sapl.crud.base import (RP_DETAIL, RP_LIST, Crud, CrudAux, from sapl.crud.base import (RP_DETAIL, RP_LIST, Crud, CrudAux,
MasterDetailCrud, MasterDetailCrud,
@ -3794,6 +3797,7 @@ class SessaoListView(ListView):
return context return context
@method_decorator(ratelimit(key='ip', rate='10/m', block=True), name='dispatch')
class PautaSessaoView(TemplateView): class PautaSessaoView(TemplateView):
model = SessaoPlenaria model = SessaoPlenaria
template_name = "sessao/pauta_inexistente.html" template_name = "sessao/pauta_inexistente.html"
@ -3809,6 +3813,7 @@ class PautaSessaoView(TemplateView):
reverse('sapl.sessao:pauta_sessao_detail', kwargs={'pk': sessao.pk})) reverse('sapl.sessao:pauta_sessao_detail', kwargs={'pk': sessao.pk}))
@method_decorator(ratelimit(key='ip', rate='10/m', block=True), name='dispatch')
class PautaSessaoDetailView(PautaMultiFormatOutputMixin, DetailView): class PautaSessaoDetailView(PautaMultiFormatOutputMixin, DetailView):
template_name = "sessao/pauta_sessao_detail.html" template_name = "sessao/pauta_sessao_detail.html"
model = SessaoPlenaria model = SessaoPlenaria

28
sapl/utils.py

@ -419,21 +419,25 @@ def get_base_url(request):
return "{0}://{1}".format(protocol, current_domain) return "{0}://{1}".format(protocol, current_domain)
def create_barcode(value, width=170, height=50): def create_barcode(value, width=170, height=50, dpi=72):
''' """
creates a base64 encoded barcode PNG image creates a base64 encoded barcode PNG image
''' """
from base64 import b64encode from base64 import b64encode
from reportlab.graphics.barcode import createBarcodeDrawing from reportlab.graphics.barcode import createBarcodeDrawing
value_bytes = bytes(value, "ascii") value_bytes = bytes(value, "ascii")
barcode = createBarcodeDrawing('Code128', barcode = createBarcodeDrawing(
value=value_bytes, 'Code128',
barWidth=width, value=value_bytes,
height=height, barWidth=width,
fontSize=2, height=height,
humanReadable=True) fontSize=2,
data = b64encode(barcode.asString('png')) humanReadable=True
return data.decode('utf-8') )
# Lower DPI prevents Cairo surface from blowing up
png_bytes = barcode.asString("png", dpi=dpi)
return b64encode(png_bytes).decode("utf-8")
YES_NO_CHOICES = [(True, _('Sim')), (False, _('Não'))] YES_NO_CHOICES = [(True, _('Sim')), (False, _('Não'))]

4
scripts/gunicorn_start.sh

@ -15,8 +15,8 @@ then
fi fi
NAME="SAPL" # Name of the application (*) NAME="SAPL" # Name of the application (*)
DJANGODIR=$SAPL_DIR/ # Django project directory (*) DJANGODIR="$SAPL_DIR/" # Django project directory (*)
SOCKFILE=$SAPL_DIR/run/gunicorn.sock # we will communicate using this unix socket (*) SOCKFILE="$SAPL_DIR/run/gunicorn.sock" # we will communicate using this unix socket (*)
USER=`whoami` # the user to run as (*) USER=`whoami` # the user to run as (*)
GROUP=`whoami` # the group to run as (*) GROUP=`whoami` # the group to run as (*)
NUM_WORKERS=3 # how many worker processes should Gunicorn spawn (*) NUM_WORKERS=3 # how many worker processes should Gunicorn spawn (*)

Loading…
Cancel
Save