Browse Source

Especifica permissoes e cria teste de verificação

pull/781/head
LeandroRoberto 8 years ago
parent
commit
d05cf6909f
  1. 27
      sapl/base/templatetags/common_tags.py
  2. 66
      sapl/materia/forms.py
  3. 6
      sapl/materia/tests/test_materia.py
  4. 5
      sapl/materia/views.py
  5. 19
      sapl/norma/migrations/0019_auto_20161028_0232.py
  6. 6
      sapl/painel/views.py
  7. 81
      sapl/rules/tests/test_rules.py
  8. 23
      sapl/sessao/views.py
  9. 49
      sapl/templates/base/appconfig_list.html
  10. 6
      sapl/utils.py

27
sapl/base/templatetags/common_tags.py

@ -3,8 +3,6 @@ from django import template
from sapl.base.models import AppConfig from sapl.base.models import AppConfig
from sapl.parlamentares.models import Filiacao from sapl.parlamentares.models import Filiacao
from sapl.utils import permissoes_adm
register = template.Library() register = template.Library()
@ -87,26 +85,6 @@ def get_delete_perm(value, arg):
return perm.__contains__(nome_app + can_delete) return perm.__contains__(nome_app + can_delete)
@register.filter
def get_doc_adm_template_perms(user):
app_config = AppConfig.objects.last()
if app_config:
if app_config.documentos_administrativos == 'O':
return True
return user.has_perms(permissoes_adm())
@register.filter
def ver_menu_sistema_perm(value):
u = value
if u.groups.filter(name='Operador Geral').exists() or u.is_superuser:
return True
else:
return False
@register.filter @register.filter
def ultima_filiacao(value): def ultima_filiacao(value):
parlamentar = value parlamentar = value
@ -120,11 +98,6 @@ def ultima_filiacao(value):
return None return None
@register.filter
def get_config_not_exists(user):
return not AppConfig.objects.exists()
@register.filter @register.filter
def get_config_attr(attribute): def get_config_attr(attribute):
return AppConfig.attr(attribute) return AppConfig.attr(attribute)

66
sapl/materia/forms.py

@ -1,8 +1,7 @@
import os
from datetime import date, datetime from datetime import date, datetime
import os
import django_filters
from crispy_forms.bootstrap import (Alert, FormActions, InlineCheckboxes, from crispy_forms.bootstrap import (Alert, FormActions, InlineCheckboxes,
InlineRadios) InlineRadios)
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
@ -18,8 +17,8 @@ from django.db.models import Max
from django.forms import ModelForm, widgets from django.forms import ModelForm, widgets
from django.forms.forms import Form from django.forms.forms import Form
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import django_filters
import sapl
from sapl.base.models import Autor from sapl.base.models import Autor
from sapl.comissoes.models import Comissao from sapl.comissoes.models import Comissao
from sapl.crispy_layout_mixin import (SaplFormLayout, form_actions, to_column, from sapl.crispy_layout_mixin import (SaplFormLayout, form_actions, to_column,
@ -33,6 +32,7 @@ from sapl.settings import MAX_DOC_UPLOAD_SIZE
from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES,
ChoiceWithoutValidationField, RangeWidgetOverride, ChoiceWithoutValidationField, RangeWidgetOverride,
autor_label, autor_modal, models_with_gr_for_model) autor_label, autor_modal, models_with_gr_for_model)
import sapl
from .models import (AcompanhamentoMateria, Anexada, Autoria, DespachoInicial, from .models import (AcompanhamentoMateria, Anexada, Autoria, DespachoInicial,
DocumentoAcessorio, MateriaLegislativa, Numeracao, DocumentoAcessorio, MateriaLegislativa, Numeracao,
@ -84,66 +84,6 @@ class UnidadeTramitacaoForm(ModelForm):
return cleaned_data return cleaned_data
class ProposicaoOldForm(ModelForm):
tipo_materia = forms.ModelChoiceField(
label=_('Matéria Vinculada'),
required=False,
queryset=TipoMateriaLegislativa.objects.all(),
empty_label='Selecione',
)
numero_materia = forms.CharField(
label='Número', required=False)
ano_materia = forms.CharField(
label='Ano', required=False)
def clean_texto_original(self):
texto_original = self.cleaned_data.get('texto_original', False)
if texto_original:
if texto_original.size > MAX_DOC_UPLOAD_SIZE:
raise ValidationError("Arquivo muito grande. ( > 5mb )")
return texto_original
def clean_data_envio(self):
data_envio = self.cleaned_data.get('data_envio') or None
if (not data_envio) and len(self.initial) > 1:
data_envio = datetime.now()
return data_envio
def clean(self):
cleaned_data = self.cleaned_data
if 'tipo' in cleaned_data:
if cleaned_data['tipo'].descricao == 'Parecer':
if self.instance.materia:
cleaned_data['materia'] = self.instance.materia
else:
try:
materia = MateriaLegislativa.objects.get(
tipo_id=cleaned_data['tipo_materia'],
ano=cleaned_data['ano_materia'],
numero=cleaned_data['numero_materia'])
except ObjectDoesNotExist:
msg = _('Matéria adicionada não existe!')
raise ValidationError(msg)
else:
cleaned_data['materia'] = materia
return cleaned_data
def save(self, commit=False):
proposicao = super(ProposicaoOldForm, self).save(commit)
if 'materia' in self.cleaned_data:
proposicao.materia = self.cleaned_data['materia']
proposicao.save()
return proposicao
class Meta:
model = Proposicao
fields = ['tipo', 'data_envio', 'descricao', 'texto_original', 'autor']
widgets = {'autor': forms.HiddenInput()}
class AcompanhamentoMateriaForm(ModelForm): class AcompanhamentoMateriaForm(ModelForm):
class Meta: class Meta:

6
sapl/materia/tests/test_materia.py

@ -1,9 +1,9 @@
import pytest
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from model_mommy import mommy from model_mommy import mommy
import pytest
from sapl.base.models import Autor, TipoAutor from sapl.base.models import Autor, TipoAutor
from sapl.comissoes.models import Comissao, TipoComissao from sapl.comissoes.models import Comissao, TipoComissao
@ -452,7 +452,7 @@ def test_proposicao_submit(admin_client):
*models_with_gr_for_model(TipoProposicao)) *models_with_gr_for_model(TipoProposicao))
for pk, mct in enumerate(mcts): for pk, mct in enumerate(mcts):
tipo_conteudo_related = mommy.make(mct, pk=pk) tipo_conteudo_related = mommy.make(mct, pk=pk + 1)
response = admin_client.post( response = admin_client.post(
reverse('sapl.materia:proposicao_create'), reverse('sapl.materia:proposicao_create'),
@ -475,7 +475,7 @@ def test_proposicao_submit(admin_client):
assert proposicao is not None assert proposicao is not None
assert proposicao.descricao == 'Teste proposição' assert proposicao.descricao == 'Teste proposição'
assert proposicao.tipo.pk == 3 assert proposicao.tipo.pk == 3
assert proposicao.tipo.tipo_conteudo_related.pk == pk assert proposicao.tipo.tipo_conteudo_related.pk == pk + 1
@pytest.mark.django_db(transaction=False) @pytest.mark.django_db(transaction=False)

5
sapl/materia/views.py

@ -5,6 +5,7 @@ from string import ascii_letters, digits
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML from crispy_forms.layout import HTML
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.core.mail import send_mail from django.core.mail import send_mail
@ -34,7 +35,7 @@ from sapl.materia.forms import (AnexadaForm, ConfirmarProposicaoForm,
from sapl.norma.models import LegislacaoCitada from sapl.norma.models import LegislacaoCitada
from sapl.utils import (TURNO_TRAMITACAO_CHOICES, YES_NO_CHOICES, autor_label, from sapl.utils import (TURNO_TRAMITACAO_CHOICES, YES_NO_CHOICES, autor_label,
autor_modal, gerar_hash_arquivo, get_base_url, autor_modal, gerar_hash_arquivo, get_base_url,
montar_row_autor, permission_required_for_app) montar_row_autor)
import sapl import sapl
from .forms import (AcessorioEmLoteFilterSet, AcompanhamentoMateriaForm, from .forms import (AcessorioEmLoteFilterSet, AcompanhamentoMateriaForm,
@ -101,7 +102,7 @@ class ProposicaoTaView(IntegracaoTaView):
return self.get_redirect_deactivated() return self.get_redirect_deactivated()
@permission_required_for_app(app_label=apps.AppConfig.label) @permission_required('materia.detail_materialegislativa')
def recuperar_materia(request): def recuperar_materia(request):
tipo = TipoMateriaLegislativa.objects.get(pk=request.GET['tipo']) tipo = TipoMateriaLegislativa.objects.get(pk=request.GET['tipo'])
ano = request.GET.get('ano', '') ano = request.GET.get('ano', '')

19
sapl/norma/migrations/0019_auto_20161028_0232.py

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-10-28 02:32
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('norma', '0018_auto_20161027_1434'),
]
operations = [
migrations.AlterUniqueTogether(
name='assuntonormarelationship',
unique_together=set([('assunto', 'norma')]),
),
]

6
sapl/painel/views.py

@ -7,6 +7,7 @@ from django.shortcuts import render
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from sapl.crud.base import Crud from sapl.crud.base import Crud
from sapl.painel.apps import AppConfig
from sapl.painel.models import Painel from sapl.painel.models import Painel
from sapl.parlamentares.models import Filiacao from sapl.parlamentares.models import Filiacao
from sapl.sessao.models import (ExpedienteMateria, OrdemDia, PresencaOrdemDia, from sapl.sessao.models import (ExpedienteMateria, OrdemDia, PresencaOrdemDia,
@ -15,11 +16,14 @@ from sapl.sessao.models import (ExpedienteMateria, OrdemDia, PresencaOrdemDia,
from .models import Cronometro from .models import Cronometro
CronometroPainelCrud = Crud.build(Cronometro, '') CronometroPainelCrud = Crud.build(Cronometro, '')
# FIXME mudar lógica
def check_permission(user): def check_permission(user):
return user.has_perms(permissoes_painel()) return user.has_module_perms(AppConfig.label)
@user_passes_test(check_permission) @user_passes_test(check_permission)

81
sapl/rules/tests/test_rules.py

@ -1,13 +1,16 @@
import pytest
from django.apps import apps from django.apps import apps
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import Permission from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.utils import six
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import pytest
from sapl.rules import SAPL_GROUPS from sapl.rules import SAPL_GROUPS
from sapl.rules.map_rules import rules_patterns from sapl.rules.map_rules import rules_patterns
from sapl.test_urls import create_perms_post_migrate from sapl.test_urls import create_perms_post_migrate
from scripts.lista_urls import lista_urls
sapl_appconfs = [apps.get_app_config(n[5:]) for n in settings.SAPL_APPS] sapl_appconfs = [apps.get_app_config(n[5:]) for n in settings.SAPL_APPS]
@ -47,7 +50,7 @@ def test_models_in_rules_patterns(model_item):
@pytest.mark.django_db(transaction=False) @pytest.mark.django_db(transaction=False)
@pytest.mark.parametrize('model_item', sapl_models) @pytest.mark.parametrize('model_item', sapl_models)
def test_permission_exists(model_item): def test_permission_of_rules_exists(model_item):
print(model_item) print(model_item)
create_perms_post_migrate(model_item._meta.app_config) create_perms_post_migrate(model_item._meta.app_config)
@ -75,3 +78,77 @@ def test_permission_exists(model_item):
assert p, _('Permissão (%s) no model (%s) não existe.') % ( assert p, _('Permissão (%s) no model (%s) não existe.') % (
codename, codename,
model_item) model_item)
_lista_urls = lista_urls()
@pytest.mark.django_db(transaction=False)
@pytest.mark.parametrize('url_item', _lista_urls)
def test_permission_required_of_views_exists(url_item):
"""
testa se, nas views que possuem atributo permission_required,
as permissões fixas escritas manualmente realmente exitem em Permission
Obs: isso não testa permissões escritas em anotações de método ou classe
"""
for app in sapl_appconfs:
# readequa permissões dos models adicionando
# list e detail permissions
create_perms_post_migrate(app)
key, url, var, app_name = url_item
url = '/' + (url % {v: 1 for v in var})
assert '\n' not in url, """
A url (%s) da app (%s) está mal formada.
""" % (app_name, url)
view = None
if hasattr(key, 'view_class'):
view = key.view_class
if hasattr(view, 'permission_required'):
if isinstance(view.permission_required, six.string_types):
perms = (view.permission_required, )
else:
perms = view.permission_required
if not perms:
return
for perm in perms:
if perm[0] == '.' and perm[-1] == '_':
model = None
if hasattr(view, 'model') and view.model:
model = view.model
elif hasattr(view, 'filterset_class'):
model = view.fielterset_class._meta.model
elif hasattr(view, 'form_class'):
model = view.form_class._meta.model
assert model, _('model %s não localizado em %s'
) % (model, view)
codename = perm[1:] + view.model._meta.model_name
else:
codename = perm
codename = codename.split('.')
if len(codename) == 1:
content_type = ContentType.objects.get_by_natural_key(
app_label=model._meta.app_label,
model=model._meta.model_name)
p = Permission.objects.filter(
content_type=content_type,
codename=codename[0]).exists()
elif len(codename) == 2:
p = Permission.objects.filter(
content_type__app_label=codename[0],
codename=codename[1]).exists()
assert p, _('Permissão (%s) na view (%s) não existe.') % (
codename,
view)

23
sapl/sessao/views.py

@ -2,6 +2,7 @@ from datetime import datetime
from re import sub from re import sub
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import permission_required
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.forms.utils import ErrorList from django.forms.utils import ErrorList
@ -28,10 +29,9 @@ from sapl.materia.views import MateriaLegislativaPesquisaView
from sapl.norma.models import NormaJuridica from sapl.norma.models import NormaJuridica
from sapl.parlamentares.models import (Legislatura, Parlamentar, from sapl.parlamentares.models import (Legislatura, Parlamentar,
SessaoLegislativa) SessaoLegislativa)
from sapl.sessao import apps
from sapl.sessao.apps import AppConfig from sapl.sessao.apps import AppConfig
from sapl.sessao.forms import ExpedienteMateriaForm, OrdemDiaForm from sapl.sessao.forms import ExpedienteMateriaForm, OrdemDiaForm
from sapl.utils import permission_required_for_app
from .forms import (AdicionarVariasMateriasFilterSet, ExpedienteForm, from .forms import (AdicionarVariasMateriasFilterSet, ExpedienteForm,
ListMateriaForm, MesaForm, OradorExpedienteForm, ListMateriaForm, MesaForm, OradorExpedienteForm,
@ -45,6 +45,7 @@ from .models import (Bancada, Bloco, CargoBancada, CargoMesa,
SessaoPlenariaPresenca, TipoExpediente, SessaoPlenariaPresenca, TipoExpediente,
TipoResultadoVotacao, TipoSessaoPlenaria, VotoParlamentar) TipoResultadoVotacao, TipoSessaoPlenaria, VotoParlamentar)
TipoSessaoCrud = CrudAux.build(TipoSessaoPlenaria, 'tipo_sessao_plenaria') TipoSessaoCrud = CrudAux.build(TipoSessaoPlenaria, 'tipo_sessao_plenaria')
TipoExpedienteCrud = CrudAux.build(TipoExpediente, 'tipo_expediente') TipoExpedienteCrud = CrudAux.build(TipoExpediente, 'tipo_expediente')
CargoBancadaCrud = CrudAux.build(CargoBancada, '') CargoBancadaCrud = CrudAux.build(CargoBancada, '')
@ -83,7 +84,7 @@ def reordernar_materias_ordem(request, pk):
reverse('sapl.sessao:ordemdia_list', kwargs={'pk': pk})) reverse('sapl.sessao:ordemdia_list', kwargs={'pk': pk}))
@permission_required_for_app(app_label=apps.AppConfig.label) @permission_required('sessao.change_expedientemateria')
def abrir_votacao_expediente_view(request, pk, spk): def abrir_votacao_expediente_view(request, pk, spk):
existe_votacao_aberta = ExpedienteMateria.objects.filter( existe_votacao_aberta = ExpedienteMateria.objects.filter(
sessao_plenaria_id=spk, votacao_aberta=True sessao_plenaria_id=spk, votacao_aberta=True
@ -100,7 +101,7 @@ def abrir_votacao_expediente_view(request, pk, spk):
reverse('sapl.sessao:expedientemateria_list', kwargs={'pk': spk})) reverse('sapl.sessao:expedientemateria_list', kwargs={'pk': spk}))
@permission_required_for_app(app_label=apps.AppConfig.label) @permission_required('sessao.change_ordemdia')
def abrir_votacao_ordem_view(request, pk, spk): def abrir_votacao_ordem_view(request, pk, spk):
existe_votacao_aberta = OrdemDia.objects.filter( existe_votacao_aberta = OrdemDia.objects.filter(
sessao_plenaria_id=spk, votacao_aberta=True sessao_plenaria_id=spk, votacao_aberta=True
@ -523,14 +524,12 @@ class PresencaView(FormMixin, PresencaMixin, DetailView):
_('Presença'), self.object) _('Presença'), self.object)
return context return context
@method_decorator(permission_required_for_app(AppConfig.label)) @method_decorator(permission_required(
'sessao.add_sessaoplenariapresenca'))
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
form = self.get_form() form = self.get_form()
if not self.request.user.has_module_perms(AppConfig.label):
return self.form_invalid(form)
if form.is_valid(): if form.is_valid():
# Pegar os presentes salvos no banco # Pegar os presentes salvos no banco
presentes_banco = SessaoPlenariaPresenca.objects.filter( presentes_banco = SessaoPlenariaPresenca.objects.filter(
@ -603,7 +602,7 @@ class PresencaOrdemDiaView(FormMixin, PresencaMixin, DetailView):
_('Presença Ordem do Dia'), self.object) _('Presença Ordem do Dia'), self.object)
return context return context
@method_decorator(permission_required_for_app(AppConfig.label)) @method_decorator(permission_required('sessao.add_presencaordemdia'))
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
@ -683,7 +682,7 @@ class ListMateriaOrdemDiaView(FormMixin, DetailView):
return self.render_to_response(context) return self.render_to_response(context)
@method_decorator(permission_required_for_app(AppConfig.label)) @method_decorator(permission_required('sessao.change_ordemdia'))
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
context = self.get_context_data(object=self.object) context = self.get_context_data(object=self.object)
@ -785,7 +784,7 @@ class MesaView(FormMixin, DetailView):
_('Mesa Diretora'), self.object) _('Mesa Diretora'), self.object)
return context return context
@method_decorator(permission_required_for_app(AppConfig.label)) @method_decorator(permission_required('sessao.change_integrantemesa'))
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
form = MesaForm(request.POST) form = MesaForm(request.POST)
@ -1040,7 +1039,7 @@ class ExpedienteView(FormMixin, DetailView):
_('Expediente Diversos'), self.object) _('Expediente Diversos'), self.object)
return context return context
@method_decorator(permission_required_for_app(AppConfig.label)) @method_decorator(permission_required('sessao.add_expedientesessao'))
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
form = ExpedienteForm(request.POST) form = ExpedienteForm(request.POST)

49
sapl/templates/base/appconfig_list.html

@ -1,49 +0,0 @@
{% extends "crud/list.html" %}
{% load i18n %}
{% load common_tags %}
{% block base_content %}
<div class="actions btn-group pull-right" role="group">
{% if user|get_config_not_exists %}
<a href="{{ view.create_url }}" class="btn btn-default">
{% blocktrans with verbose_name=view.verbose_name %} Adicionar {{ verbose_name }} {% endblocktrans %}
</a>
{% endif %}
{% block more_buttons %}{% endblock more_buttons %}
</div>
<br/><br/>
{% block extra_content %} {% endblock %}
{% if not rows %}
<p>{{ NO_ENTRIES_MSG }}</p>
{% else %}
<table class="table table-striped table-hover">
<thead>
<tr>
{% for name in headers %}
<th>{{ name }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for value_list in rows %}
<tr>
{% for value, href in value_list %}
<td>
{% if href %}
<a href="{{ href }}">{{ value }}</a>
{% else %}
{{ value|safe }}
{% endif %}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% include "paginacao.html" %}
{% endblock %}

6
sapl/utils.py

@ -395,17 +395,16 @@ def permissoes(nome_grupo, app_label):
except: except:
pass pass
return set(lista_permissoes) return set(lista_permissoes)
"""
def permission_required_for_app(app_label, login_url=None, def permission_required_for_app(app_label, login_url=None,
raise_exception=False): raise_exception=False):
"""
Decorator for views that checks whether a user has a particular permission Decorator for views that checks whether a user has a particular permission
enabled, redirecting to the log-in page if necessary. enabled, redirecting to the log-in page if necessary.
If the raise_exception parameter is given the PermissionDenied exception If the raise_exception parameter is given the PermissionDenied exception
is raised. is raised.
"""
def check_perms(user): def check_perms(user):
if user.has_module_perms(app_label): if user.has_module_perms(app_label):
return True return True
@ -416,7 +415,6 @@ def permission_required_for_app(app_label, login_url=None,
return False return False
return user_passes_test(check_perms, login_url=login_url) return user_passes_test(check_perms, login_url=login_url)
"""
def permissoes_materia(): def permissoes_materia():
return permissoes('Operador de Matéria', 'materia') return permissoes('Operador de Matéria', 'materia')

Loading…
Cancel
Save