diff --git a/sapl/base/templatetags/common_tags.py b/sapl/base/templatetags/common_tags.py index 60cb80f62..d023104d5 100644 --- a/sapl/base/templatetags/common_tags.py +++ b/sapl/base/templatetags/common_tags.py @@ -3,8 +3,6 @@ from django import template from sapl.base.models import AppConfig from sapl.parlamentares.models import Filiacao -from sapl.utils import permissoes_adm - register = template.Library() @@ -87,26 +85,6 @@ def get_delete_perm(value, arg): 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 def ultima_filiacao(value): parlamentar = value @@ -120,11 +98,6 @@ def ultima_filiacao(value): return None -@register.filter -def get_config_not_exists(user): - return not AppConfig.objects.exists() - - @register.filter def get_config_attr(attribute): return AppConfig.attr(attribute) diff --git a/sapl/materia/forms.py b/sapl/materia/forms.py index acfa37f16..8ba0e5477 100644 --- a/sapl/materia/forms.py +++ b/sapl/materia/forms.py @@ -1,8 +1,7 @@ -import os from datetime import date, datetime +import os -import django_filters from crispy_forms.bootstrap import (Alert, FormActions, InlineCheckboxes, InlineRadios) 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.forms import Form from django.utils.translation import ugettext_lazy as _ +import django_filters -import sapl from sapl.base.models import Autor from sapl.comissoes.models import Comissao 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, ChoiceWithoutValidationField, RangeWidgetOverride, autor_label, autor_modal, models_with_gr_for_model) +import sapl from .models import (AcompanhamentoMateria, Anexada, Autoria, DespachoInicial, DocumentoAcessorio, MateriaLegislativa, Numeracao, @@ -84,66 +84,6 @@ class UnidadeTramitacaoForm(ModelForm): 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 Meta: @@ -953,13 +893,13 @@ class ProposicaoForm(forms.ModelForm): if self.instance.materia_de_vinculo: self.fields[ 'tipo_materia' - ].initial = self.instance.materia_de_vinculo.tipo + ].initial = self.instance.materia_de_vinculo.tipo self.fields[ 'numero_materia' - ].initial = self.instance.materia_de_vinculo.numero + ].initial = self.instance.materia_de_vinculo.numero self.fields[ 'ano_materia' - ].initial = self.instance.materia_de_vinculo.ano + ].initial = self.instance.materia_de_vinculo.ano def clean_texto_original(self): texto_original = self.cleaned_data.get('texto_original', False) @@ -1133,13 +1073,13 @@ class ConfirmarProposicaoForm(ProposicaoForm): if self.instance.materia_de_vinculo: self.fields[ 'tipo_materia' - ].initial = self.instance.materia_de_vinculo.tipo + ].initial = self.instance.materia_de_vinculo.tipo self.fields[ 'numero_materia' - ].initial = self.instance.materia_de_vinculo.numero + ].initial = self.instance.materia_de_vinculo.numero self.fields[ 'ano_materia' - ].initial = self.instance.materia_de_vinculo.ano + ].initial = self.instance.materia_de_vinculo.ano if self.proposicao_incorporacao_obrigatoria == 'C': self.fields['gerar_protocolo'].initial = True @@ -1156,7 +1096,7 @@ class ConfirmarProposicaoForm(ProposicaoForm): _('Regimente de Tramitação deve ser informado.')) elif self.instance.tipo.content_type.model_class( - ) == TipoDocumento and not cd['materia_de_vinculo']: + ) == TipoDocumento and not cd['materia_de_vinculo']: raise ValidationError( _('Documentos não podem ser incorporados sem definir ' @@ -1224,7 +1164,7 @@ class ConfirmarProposicaoForm(ProposicaoForm): conteudo_gerado = None if self.instance.tipo.content_type.model_class( - ) == TipoMateriaLegislativa: + ) == TipoMateriaLegislativa: numero__max = MateriaLegislativa.objects.filter( tipo=proposicao.tipo.tipo_conteudo_related, ano=datetime.now().year).aggregate(Max('numero')) @@ -1358,7 +1298,7 @@ class ConfirmarProposicaoForm(ProposicaoForm): protocolo.anulado = False if self.instance.tipo.content_type.model_class( - ) == TipoMateriaLegislativa: + ) == TipoMateriaLegislativa: protocolo.tipo_materia = proposicao.tipo.tipo_conteudo_related elif self.instance.tipo.content_type.model_class() == TipoDocumento: protocolo.tipo_documento = proposicao.tipo.tipo_conteudo_related diff --git a/sapl/materia/tests/test_materia.py b/sapl/materia/tests/test_materia.py index 19c0c9fa6..5455792f4 100644 --- a/sapl/materia/tests/test_materia.py +++ b/sapl/materia/tests/test_materia.py @@ -1,9 +1,9 @@ -import pytest from django.contrib.auth import get_user_model from django.contrib.contenttypes.models import ContentType from django.core.files.uploadedfile import SimpleUploadedFile from django.core.urlresolvers import reverse from model_mommy import mommy +import pytest from sapl.base.models import Autor, TipoAutor from sapl.comissoes.models import Comissao, TipoComissao @@ -452,7 +452,7 @@ def test_proposicao_submit(admin_client): *models_with_gr_for_model(TipoProposicao)) 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( reverse('sapl.materia:proposicao_create'), @@ -475,7 +475,7 @@ def test_proposicao_submit(admin_client): assert proposicao is not None assert proposicao.descricao == 'Teste proposição' 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) diff --git a/sapl/materia/views.py b/sapl/materia/views.py index 71f8bb829..30d038f23 100644 --- a/sapl/materia/views.py +++ b/sapl/materia/views.py @@ -5,6 +5,7 @@ from string import ascii_letters, digits from crispy_forms.helper import FormHelper from crispy_forms.layout import HTML from django.contrib import messages +from django.contrib.auth.decorators import permission_required from django.contrib.auth.mixins import PermissionRequiredMixin from django.core.exceptions import ObjectDoesNotExist 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.utils import (TURNO_TRAMITACAO_CHOICES, YES_NO_CHOICES, autor_label, autor_modal, gerar_hash_arquivo, get_base_url, - montar_row_autor, permission_required_for_app) + montar_row_autor) import sapl from .forms import (AcessorioEmLoteFilterSet, AcompanhamentoMateriaForm, @@ -101,7 +102,7 @@ class ProposicaoTaView(IntegracaoTaView): return self.get_redirect_deactivated() -@permission_required_for_app(app_label=apps.AppConfig.label) +@permission_required('materia.detail_materialegislativa') def recuperar_materia(request): tipo = TipoMateriaLegislativa.objects.get(pk=request.GET['tipo']) ano = request.GET.get('ano', '') diff --git a/sapl/norma/migrations/0019_auto_20161028_0232.py b/sapl/norma/migrations/0019_auto_20161028_0232.py new file mode 100644 index 000000000..075607017 --- /dev/null +++ b/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')]), + ), + ] diff --git a/sapl/painel/views.py b/sapl/painel/views.py index d8e2a7146..d43555f51 100644 --- a/sapl/painel/views.py +++ b/sapl/painel/views.py @@ -7,6 +7,7 @@ from django.shortcuts import render from django.utils.translation import ugettext_lazy as _ from sapl.crud.base import Crud +from sapl.painel.apps import AppConfig from sapl.painel.models import Painel from sapl.parlamentares.models import Filiacao from sapl.sessao.models import (ExpedienteMateria, OrdemDia, PresencaOrdemDia, @@ -15,11 +16,14 @@ from sapl.sessao.models import (ExpedienteMateria, OrdemDia, PresencaOrdemDia, from .models import Cronometro + CronometroPainelCrud = Crud.build(Cronometro, '') +# FIXME mudar lógica + def check_permission(user): - return user.has_perms(permissoes_painel()) + return user.has_module_perms(AppConfig.label) @user_passes_test(check_permission) diff --git a/sapl/rules/tests/test_rules.py b/sapl/rules/tests/test_rules.py index 359c7d6d8..e03bf85d0 100644 --- a/sapl/rules/tests/test_rules.py +++ b/sapl/rules/tests/test_rules.py @@ -1,13 +1,16 @@ -import pytest from django.apps import apps from django.conf import settings from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType +from django.utils import six from django.utils.translation import ugettext_lazy as _ +import pytest from sapl.rules import SAPL_GROUPS from sapl.rules.map_rules import rules_patterns 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] @@ -47,7 +50,7 @@ def test_models_in_rules_patterns(model_item): @pytest.mark.django_db(transaction=False) @pytest.mark.parametrize('model_item', sapl_models) -def test_permission_exists(model_item): +def test_permission_of_rules_exists(model_item): print(model_item) 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.') % ( codename, 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) diff --git a/sapl/sessao/views.py b/sapl/sessao/views.py index f3e39c46c..f36381233 100644 --- a/sapl/sessao/views.py +++ b/sapl/sessao/views.py @@ -2,6 +2,7 @@ from datetime import datetime from re import sub from django.contrib import messages +from django.contrib.auth.decorators import permission_required from django.core.exceptions import ObjectDoesNotExist from django.core.urlresolvers import reverse from django.forms.utils import ErrorList @@ -28,10 +29,9 @@ from sapl.materia.views import MateriaLegislativaPesquisaView from sapl.norma.models import NormaJuridica from sapl.parlamentares.models import (Legislatura, Parlamentar, SessaoLegislativa) -from sapl.sessao import apps + from sapl.sessao.apps import AppConfig from sapl.sessao.forms import ExpedienteMateriaForm, OrdemDiaForm -from sapl.utils import permission_required_for_app from .forms import (AdicionarVariasMateriasFilterSet, ExpedienteForm, ListMateriaForm, MesaForm, OradorExpedienteForm, @@ -45,6 +45,7 @@ from .models import (Bancada, Bloco, CargoBancada, CargoMesa, SessaoPlenariaPresenca, TipoExpediente, TipoResultadoVotacao, TipoSessaoPlenaria, VotoParlamentar) + TipoSessaoCrud = CrudAux.build(TipoSessaoPlenaria, 'tipo_sessao_plenaria') TipoExpedienteCrud = CrudAux.build(TipoExpediente, 'tipo_expediente') CargoBancadaCrud = CrudAux.build(CargoBancada, '') @@ -83,7 +84,7 @@ def reordernar_materias_ordem(request, 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): existe_votacao_aberta = ExpedienteMateria.objects.filter( 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})) -@permission_required_for_app(app_label=apps.AppConfig.label) +@permission_required('sessao.change_ordemdia') def abrir_votacao_ordem_view(request, pk, spk): existe_votacao_aberta = OrdemDia.objects.filter( sessao_plenaria_id=spk, votacao_aberta=True @@ -523,14 +524,12 @@ class PresencaView(FormMixin, PresencaMixin, DetailView): _('Presença'), self.object) return context - @method_decorator(permission_required_for_app(AppConfig.label)) + @method_decorator(permission_required( + 'sessao.add_sessaoplenariapresenca')) def post(self, request, *args, **kwargs): self.object = self.get_object() form = self.get_form() - if not self.request.user.has_module_perms(AppConfig.label): - return self.form_invalid(form) - if form.is_valid(): # Pegar os presentes salvos no banco presentes_banco = SessaoPlenariaPresenca.objects.filter( @@ -603,7 +602,7 @@ class PresencaOrdemDiaView(FormMixin, PresencaMixin, DetailView): _('Presença Ordem do Dia'), self.object) return context - @method_decorator(permission_required_for_app(AppConfig.label)) + @method_decorator(permission_required('sessao.add_presencaordemdia')) def post(self, request, *args, **kwargs): self.object = self.get_object() @@ -683,7 +682,7 @@ class ListMateriaOrdemDiaView(FormMixin, DetailView): 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): self.object = self.get_object() context = self.get_context_data(object=self.object) @@ -785,7 +784,7 @@ class MesaView(FormMixin, DetailView): _('Mesa Diretora'), self.object) return context - @method_decorator(permission_required_for_app(AppConfig.label)) + @method_decorator(permission_required('sessao.change_integrantemesa')) def post(self, request, *args, **kwargs): self.object = self.get_object() form = MesaForm(request.POST) @@ -1040,7 +1039,7 @@ class ExpedienteView(FormMixin, DetailView): _('Expediente Diversos'), self.object) return context - @method_decorator(permission_required_for_app(AppConfig.label)) + @method_decorator(permission_required('sessao.add_expedientesessao')) def post(self, request, *args, **kwargs): self.object = self.get_object() form = ExpedienteForm(request.POST) diff --git a/sapl/templates/base/appconfig_list.html b/sapl/templates/base/appconfig_list.html deleted file mode 100644 index 3ffcc7f4b..000000000 --- a/sapl/templates/base/appconfig_list.html +++ /dev/null @@ -1,49 +0,0 @@ -{% extends "crud/list.html" %} -{% load i18n %} -{% load common_tags %} - -{% block base_content %} -
{{ NO_ENTRIES_MSG }}
-{% else %} -{{ name }} | - {% endfor %} -
---|
- {% if href %} - {{ value }} - {% else %} - {{ value|safe }} - {% endif %} - | - {% endfor %} -