Browse Source

Fix #130 Implementa a API Rest no Sapl (#2393)

* impl api rest full automática

* ajuste django-filter

* ajusta filter para FileField
pull/2490/head
Leandro Roberto da Silva 6 years ago
committed by GitHub
parent
commit
8c77e216e4
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 39
      sapl/api/permissions.py
  2. 6
      sapl/api/serializers.py
  3. 19
      sapl/api/urls.py
  4. 326
      sapl/api/views.py
  5. 5
      sapl/rules/apps.py
  6. 310
      sapl/rules/map_rules.py
  7. 6
      sapl/settings.py

39
sapl/api/permissions.py

@ -1,7 +1,8 @@
from rest_framework.permissions import DjangoModelPermissions from rest_framework.permissions import DjangoModelPermissions
from sapl.rules.map_rules import rules_patterns_public
class DjangoModelPermissions(DjangoModelPermissions): class SaplModelPermissions(DjangoModelPermissions):
perms_map = { perms_map = {
'GET': ['%(app_label)s.list_%(model_name)s', 'GET': ['%(app_label)s.list_%(model_name)s',
@ -10,9 +11,43 @@ class DjangoModelPermissions(DjangoModelPermissions):
'%(app_label)s.detail_%(model_name)s'], '%(app_label)s.detail_%(model_name)s'],
'HEAD': ['%(app_label)s.list_%(model_name)s', 'HEAD': ['%(app_label)s.list_%(model_name)s',
'%(app_label)s.detail_%(model_name)s'], '%(app_label)s.detail_%(model_name)s'],
'POST': ['%(app_label)s.list_%(model_name)s'], 'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'], 'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'], 'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'], 'DELETE': ['%(app_label)s.delete_%(model_name)s'],
} }
def has_permission(self, request, view):
if getattr(view, '_ignore_model_permissions', False):
return True
if hasattr(view, 'get_queryset'):
queryset = view.get_queryset()
else:
queryset = getattr(view, 'queryset', None)
assert queryset is not None, (
'Cannot apply DjangoModelPermissions on a view that '
'does not set `.queryset` or have a `.get_queryset()` method.'
)
perms = self.get_required_permissions(request.method, queryset.model)
key = '{}:{}'.format(
queryset.model._meta.app_label,
queryset.model._meta.model_name)
if key in rules_patterns_public:
perms = set(perms)
perms_publicas = rules_patterns_public[key]
private_perms = perms - perms_publicas
if not private_perms:
return True
return (
request.user and
(request.user.is_authenticated() or not self.authenticated_users_only) and
request.user.has_perms(perms)
)

6
sapl/api/serializers.py

@ -49,14 +49,14 @@ class AutorSerializer(serializers.ModelSerializer):
fields = '__all__' fields = '__all__'
class MateriaLegislativaSerializer(serializers.ModelSerializer): class MateriaLegislativaOldSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = MateriaLegislativa model = MateriaLegislativa
fields = '__all__' fields = '__all__'
class SessaoPlenariaSerializer(serializers.ModelSerializer): class SessaoPlenariaOldSerializer(serializers.ModelSerializer):
codReuniao = serializers.SerializerMethodField('get_pk_sessao') codReuniao = serializers.SerializerMethodField('get_pk_sessao')
codReuniaoPrincipal = serializers.SerializerMethodField('get_pk_sessao') codReuniaoPrincipal = serializers.SerializerMethodField('get_pk_sessao')
@ -109,7 +109,7 @@ class SessaoPlenariaSerializer(serializers.ModelSerializer):
) )
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(SessaoPlenariaSerializer, self).__init__(args, kwargs) super(SessaoPlenariaOldSerializer, self).__init__(args, kwargs)
def get_pk_sessao(self, obj): def get_pk_sessao(self, obj):
return obj.pk return obj.pk

19
sapl/api/urls.py

@ -4,7 +4,8 @@ from rest_framework.routers import DefaultRouter
from sapl.api.views import (AutoresPossiveisListView, AutoresProvaveisListView, from sapl.api.views import (AutoresPossiveisListView, AutoresProvaveisListView,
AutorListView, MateriaLegislativaViewSet, AutorListView, MateriaLegislativaViewSet,
ModelChoiceView, SessaoPlenariaViewSet) ModelChoiceView, SessaoPlenariaViewSet,
SaplSetViews)
from .apps import AppConfig from .apps import AppConfig
@ -12,10 +13,18 @@ app_name = AppConfig.name
router = DefaultRouter() router = DefaultRouter()
router.register(r'materia', MateriaLegislativaViewSet) router.register(r'materia$', MateriaLegislativaViewSet)
router.register(r'sessao-plenaria', SessaoPlenariaViewSet) router.register(r'sessao-plenaria', SessaoPlenariaViewSet)
for app, built_sets in SaplSetViews.items():
for view_prefix, viewset in built_sets.items():
router.register(app + '/' + view_prefix, viewset)
urlpatterns_router = router.urls urlpatterns_router = router.urls
urlpatterns_api = [ urlpatterns_api = [
url(r'^autor/provaveis', url(r'^autor/provaveis',
@ -36,5 +45,9 @@ if settings.DEBUG:
urlpatterns = [ urlpatterns = [
url(r'^api/', include(urlpatterns_api)), url(r'^api/', include(urlpatterns_api)),
url(r'^api/', include(urlpatterns_router)) url(r'^api/', include(urlpatterns_router)),
# implementar caminho para autenticação
# https://www.django-rest-framework.org/tutorial/4-authentication-and-permissions/
# url(r'^api/auth/', include('rest_framework.urls', namespace='rest_framework')),
] ]

326
sapl/api/views.py

@ -1,25 +1,42 @@
import logging import logging
from django import apps
from django.conf import settings
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db.models import Q from django.db.models import Q
from django.db.models import Q
from django.db.models.fields.files import FileField
from django.http import Http404 from django.http import Http404
from django.utils.decorators import classonlymethod
from django.utils.text import capfirst
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
import django_filters
from django_filters.rest_framework.backends import DjangoFilterBackend from django_filters.rest_framework.backends import DjangoFilterBackend
from django_filters.rest_framework.filterset import FilterSet
from django_filters.utils import resolve_field
from rest_framework import serializers as rest_serializers
from rest_framework.decorators import list_route, detail_route
from rest_framework.generics import ListAPIView from rest_framework.generics import ListAPIView
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin from rest_framework.mixins import ListModelMixin, RetrieveModelMixin
from rest_framework.permissions import (AllowAny, IsAuthenticated, from rest_framework.permissions import (IsAuthenticated,
IsAuthenticatedOrReadOnly) IsAuthenticatedOrReadOnly, AllowAny)
from rest_framework.viewsets import GenericViewSet from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from sapl.api.forms import (AutorChoiceFilterSet, AutoresPossiveisFilterSet, from sapl.api.forms import (AutorChoiceFilterSet, AutoresPossiveisFilterSet,
AutorSearchForFieldFilterSet) AutorSearchForFieldFilterSet)
from sapl.api.permissions import SaplModelPermissions
from sapl.api.serializers import (AutorChoiceSerializer, AutorSerializer, from sapl.api.serializers import (AutorChoiceSerializer, AutorSerializer,
ChoiceSerializer, ChoiceSerializer,
MateriaLegislativaSerializer,
ModelChoiceSerializer, ModelChoiceSerializer,
SessaoPlenariaSerializer) SessaoPlenariaOldSerializer,
from sapl.base.models import Autor, TipoAutor MateriaLegislativaOldSerializer)
from sapl.materia.models import MateriaLegislativa from sapl.base.models import TipoAutor, Autor
from sapl.comissoes.models import Comissao
from sapl.materia.models import MateriaLegislativa, Proposicao
from sapl.parlamentares.models import Parlamentar
from sapl.rules.map_rules import __base__
from sapl.sessao.models import SessaoPlenaria from sapl.sessao.models import SessaoPlenaria
from sapl.utils import SaplGenericRelation from sapl.utils import SaplGenericRelation
@ -262,7 +279,7 @@ class MateriaLegislativaViewSet(ListModelMixin,
GenericViewSet): GenericViewSet):
permission_classes = (IsAuthenticated,) permission_classes = (IsAuthenticated,)
serializer_class = MateriaLegislativaSerializer serializer_class = MateriaLegislativaOldSerializer
queryset = MateriaLegislativa.objects.all() queryset = MateriaLegislativa.objects.all()
filter_backends = (DjangoFilterBackend,) filter_backends = (DjangoFilterBackend,)
filter_fields = ('numero', 'ano', 'tipo', ) filter_fields = ('numero', 'ano', 'tipo', )
@ -273,7 +290,298 @@ class SessaoPlenariaViewSet(ListModelMixin,
GenericViewSet): GenericViewSet):
permission_classes = (AllowAny,) permission_classes = (AllowAny,)
serializer_class = SessaoPlenariaSerializer serializer_class = SessaoPlenariaOldSerializer
queryset = SessaoPlenaria.objects.all() queryset = SessaoPlenaria.objects.all()
filter_backends = (DjangoFilterBackend,) filter_backends = (DjangoFilterBackend,)
filter_fields = ('data_inicio', 'data_fim', 'interativa') filter_fields = ('data_inicio', 'data_fim', 'interativa')
class SaplApiViewSetConstrutor(ModelViewSet):
filter_backends = (DjangoFilterBackend,)
@classonlymethod
def build_class(cls):
import inspect
from sapl.api import serializers
# Carrega todas as classes de sapl.api.serializers que possuam
# "Serializer" como Sufixo.
serializers_classes = inspect.getmembers(serializers)
serializers_classes = {i[0]: i[1] for i in filter(
lambda x: x[0].endswith('Serializer'),
serializers_classes
)}
# Carrega todas as classes de sapl.api.forms que possuam
# "FilterSet" como Sufixo.
from sapl.api import forms
filters_classes = inspect.getmembers(forms)
filters_classes = {i[0]: i[1] for i in filter(
lambda x: x[0].endswith('FilterSet'),
filters_classes
)}
built_sets = {}
def build(_model):
object_name = _model._meta.object_name
# Caso Exista, pega a classe sapl.api.serializers.{model}Serializer
serializer_name = '{model}Serializer'.format(model=object_name)
_serializer_class = serializers_classes.get(serializer_name, None)
# Caso Exista, pega a classe sapl.api.forms.{model}FilterSet
filter_name = '{model}FilterSet'.format(model=object_name)
_filter_class = filters_classes.get(filter_name, None)
def create_class():
# Define uma classe padrão para serializer caso não tenha sido
# criada a classe sapl.api.serializers.{model}Serializer
class SaplSerializer(rest_serializers.ModelSerializer):
class Meta:
model = _model
fields = '__all__'
# Define uma classe padrão para filtro caso não tenha sido
# criada a classe sapl.api.forms.{model}FilterSet
class SaplFilterSet(FilterSet):
class Meta:
model = _model
fields = '__all__'
filter_overrides = {
FileField: {
'filter_class': django_filters.CharFilter,
'extra': lambda f: {
'lookup_expr': 'exact',
},
},
}
@classmethod
def filter_for_field(cls, f, name, lookup_expr='exact'):
# Redefine método estático para ignorar filtro para
# fields que não possuam lookup_expr informado
f, lookup_type = resolve_field(f, lookup_expr)
default = {
'field_name': name,
'label': capfirst(f.verbose_name),
'lookup_expr': lookup_expr
}
filter_class, params = cls.filter_for_lookup(
f, lookup_type)
default.update(params)
if filter_class is not None:
return filter_class(**default)
return None
# Define uma classe padrão ModelViewSet de DRF
class ModelSaplViewSet(cls):
queryset = _model.objects.all()
# Utiliza o filtro customizado pela classe
# sapl.api.forms.{model}FilterSet
# ou utiliza o trivial SaplFilterSet definido acima
filter_class = _filter_class \
if _filter_class else SaplFilterSet
# Utiliza o serializer customizado pela classe
# sapl.api.serializers.{model}Serializer
# ou utiliza o trivial SaplSerializer definido acima
serializer_class = _serializer_class \
if _serializer_class else SaplSerializer
return ModelSaplViewSet
viewset = create_class()
viewset.__name__ = '%sModelSaplViewSet' % _model.__name__
return viewset
apps_sapl = [apps.apps.get_app_config(
n[5:]) for n in settings.SAPL_APPS]
for app in apps_sapl:
built_sets[app.label] = {}
for model in app.get_models():
built_sets[app.label][model._meta.model_name] = build(model)
return built_sets
"""
1. Constroi uma rest_framework.viewsets.ModelViewSet para
todos os models de todas as apps do sapl
2. Define DjangoFilterBackend como ferramenta de filtro dos campos
3. Define Serializer como a seguir:
3.1 - Define um Serializer genérico para cada módel
3.2 - Recupera Serializer customizado em sapl.api.serializers
3.3 - Para todo model é opcional a existência de
sapl.api.serializers.{model}Serializer.
Caso não seja definido um Serializer customizado, utiliza-se o trivial
4. Define um FilterSet como a seguir:
4.1 - Define um FilterSet genérico para cada módel
4.2 - Recupera FilterSet customizado em sapl.api.forms
4.3 - Para todo model é opcional a existência de
sapl.api.forms.{model}FilterSet.
Caso não seja definido um FilterSet customizado, utiliza-se o trivial
4.4 - todos os campos que aceitam lookup 'exact'
podem ser filtrados por default
5. SaplApiViewSetConstrutor não cria padrões e/ou exige conhecimento alem dos
exigidos pela DRF.
6. As rotas são criadas seguindo nome da app e nome do model
http://localhost:9000/api/{applabel}/{model_name}/
e seguem as variações definidas em:
https://www.django-rest-framework.org/api-guide/routers/#defaultrouter
7. Todas as viewsets construídas por SaplApiViewSetConstrutor e suas rotas
(paginate list, detail, edit, create, delete)
bem como testes em ambiente de desenvolvimento podem ser conferidas em:
http://localhost:9000/api/
desde que settings.DEBUG=True
**SaplSetViews** é um dict de dicts de models conforme:
{
...
'audiencia': {
'tipoaudienciapublica': TipoAudienciaPublicaViewSet,
'audienciapublica': AudienciaPublicaViewSet,
'anexoaudienciapublica': AnexoAudienciaPublicaViewSet
...
},
...
'base': {
'casalegislativa': CasaLegislativaViewSet,
'appconfig': AppConfigViewSet,
...
}
...
}
"""
SaplSetViews = SaplApiViewSetConstrutor.build_class()
# Toda Classe construida acima, pode ser redefinida e aplicado quaisquer
# das possibilidades para uma classe normal criada a partir de
# rest_framework.viewsets.ModelViewSet conforme exemplo para a classe autor
# ALGUNS EXEMPLOS
class _AutorViewSet(SaplSetViews['base']['autor']):
# OBS: esta classe é um exemplo e não contempla uma customização completa.
"""
Neste exemplo de customização do que foi criado em
SaplApiViewSetConstrutor além do ofertado por
rest_framework.viewsets.ModelViewSet, dentre outras customizações
possíveis, foi adicionado mais duas rotas, que neste exemplo seria:
padrão de ModelViewSet
http://localhost:9000/api/base/autor/ POST - create
http://localhost:9000/api/base/autor/ GET - list
http://localhost:9000/api/base/autor/{pk}/ GET - detail
http://localhost:9000/api/base/autor/{pk}/ PUT - update
http://localhost:9000/api/base/autor/{pk}/ DELETE - destroy
rotas desta classe local:
http://localhost:9000/api/base/autor/parlamentares
devolve apenas autores que são parlamentares
http://localhost:9000/api/base/autor/comissoes
devolve apenas autores que são comissões
estas mesmas listas oferecidas conforme acima, poderiam ser pesquisadas
sabendo a informação que propicia seu filtro através, pois do django_filter
no caso o ambiente de desenvolvimento no momento da escrita desse how-to:
http://localhost:9000/api/base/autor/?content_type=26 para parlamentares
http://localhost:9000/api/base/autor/?content_type=37 para comissoes
diferenças como estas podem ser crusciais para uso da api
neste caso em específico, content_types não são públicos e não possuem
clareza
isso:
http://localhost:9000/api/base/autor/parlamentares
faz o mesmo que isso:
http://localhost:9000/api/base/autor/?content_type=26
mas o primeiro é indiscutivelmente de melhor compreensão.
"""
def list_for_content_type(self, content_type):
qs = self.get_queryset()
qs = qs.filter(content_type=content_type)
page = self.paginate_queryset(qs)
if page is not None:
serializer = self.serializer_class(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(page, many=True)
return Response(serializer.data)
@list_route()
def parlamentares(self, request, *args, **kwargs):
# list /api/base/autor/parlamentares
content_type = ContentType.objects.get_for_model(Parlamentar)
return self.list_for_content_type(content_type)
@list_route()
def comissoes(self, request, *args, **kwargs):
# list /api/base/autor/comissoes
content_type = ContentType.objects.get_for_model(Comissao)
return self.list_for_content_type(content_type)
# Com isso redefinimos AutorViewSet com mais duas rotas
# além das rotas padrão
class _ParlamentarViewSet(SaplSetViews['parlamentares']['parlamentar']):
@detail_route()
def proposicoes(self, request, *args, **kwargs):
# /api/parlamentares/parlamentar/{pk}/proposicoes/
# recupera proposições enviadas e incorporadas do parlamentar
# deve coincidir com
# /parlamentar/{pk}/proposicao
content_type = ContentType.objects.get_for_model(Parlamentar)
qs = Proposicao.objects.filter(
data_envio__isnull=False,
data_recebimento__isnull=False,
cancelado=False,
autor__object_id=kwargs['pk'],
autor__content_type=content_type
)
page = self.paginate_queryset(qs)
if page is not None:
serializer = SaplSetViews[
'materia']['proposicao'].serializer_class(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(page, many=True)
return Response(serializer.data)
class _ProposicaoViewSet(SaplSetViews['materia']['proposicao']):
def get_queryset(self):
qs = super().get_queryset()
if self.request.user.is_anonymous():
return qs.none()
qs = qs.filter(autor__user=self.request.user)
return qs
SaplSetViews['base']['autor'] = _AutorViewSet
SaplSetViews['materia']['proposicao'] = _ProposicaoViewSet
SaplSetViews['parlamentares']['parlamentar'] = _ParlamentarViewSet

5
sapl/rules/apps.py

@ -1,8 +1,7 @@
from builtins import LookupError from builtins import LookupError
import django
import logging import logging
import django
from django.apps import apps from django.apps import apps
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.auth.management import _get_all_permissions from django.contrib.auth.management import _get_all_permissions
@ -177,7 +176,7 @@ def get_rules():
try: try:
logger.info("Tentando associar grupos.") logger.info("Tentando associar grupos.")
print(' ', group_name) print(' ', group_name)
for model, perms in rules_list: for model, perms, perms_publicas in rules_list:
self.associar(group, model, perms) self.associar(group, model, perms)
except Exception as e: except Exception as e:
logger.error(str(e)) logger.error(str(e))

310
sapl/rules/map_rules.py

@ -50,23 +50,27 @@ from sapl.sessao import models as sessao
__base__ = [RP_LIST, RP_DETAIL, RP_ADD, RP_CHANGE, RP_DELETE] __base__ = [RP_LIST, RP_DETAIL, RP_ADD, RP_CHANGE, RP_DELETE]
__listdetailchange__ = [RP_LIST, RP_DETAIL, RP_CHANGE] __listdetailchange__ = [RP_LIST, RP_DETAIL, RP_CHANGE]
__perms_publicas__ = {RP_LIST, RP_DETAIL}
rules_group_administrativo = { rules_group_administrativo = {
'group': SAPL_GROUP_ADMINISTRATIVO, 'group': SAPL_GROUP_ADMINISTRATIVO,
'rules': [ 'rules': [
(materia.MateriaLegislativa, ['can_access_impressos']), (materia.MateriaLegislativa, [
(protocoloadm.DocumentoAdministrativo, __base__), 'can_access_impressos'], __perms_publicas__),
(protocoloadm.DocumentoAcessorioAdministrativo, __base__), # TODO: tratar em sapl.api a questão de ostencivo e restritivo
(protocoloadm.TramitacaoAdministrativo, __base__), (protocoloadm.DocumentoAdministrativo, __base__, set()),
(protocoloadm.DocumentoAcessorioAdministrativo, __base__, set()),
(protocoloadm.TramitacaoAdministrativo, __base__, set()),
] ]
} }
rules_group_audiencia = { rules_group_audiencia = {
'group': SAPL_GROUP_GERAL, 'group': SAPL_GROUP_GERAL,
'rules': [ 'rules': [
(audiencia.AudienciaPublica, __base__), (audiencia.AudienciaPublica, __base__, __perms_publicas__),
(audiencia.TipoAudienciaPublica, __base__), (audiencia.TipoAudienciaPublica, __base__, __perms_publicas__),
(audiencia.AnexoAudienciaPublica, __base__), (audiencia.AnexoAudienciaPublica, __base__, __perms_publicas__),
] ]
} }
@ -75,49 +79,52 @@ rules_group_protocolo = {
'group': SAPL_GROUP_PROTOCOLO, 'group': SAPL_GROUP_PROTOCOLO,
'rules': [ 'rules': [
(protocoloadm.Protocolo, __base__ + [ (protocoloadm.Protocolo, __base__ + [
'action_anular_protocolo']), 'action_anular_protocolo'], set()),
(protocoloadm.DocumentoAdministrativo, (protocoloadm.DocumentoAdministrativo,
[RP_ADD] + __listdetailchange__), [RP_ADD] + __listdetailchange__, set()),
(protocoloadm.DocumentoAcessorioAdministrativo, __listdetailchange__), (protocoloadm.DocumentoAcessorioAdministrativo, __listdetailchange__, set()),
(materia.MateriaLegislativa, __listdetailchange__), (materia.MateriaLegislativa, __listdetailchange__, __perms_publicas__),
(materia.MateriaLegislativa, ['can_access_impressos']), (materia.MateriaLegislativa, [
(materia.DocumentoAcessorio, __listdetailchange__), 'can_access_impressos'], __perms_publicas__),
(materia.Anexada, __base__), (materia.DocumentoAcessorio, __listdetailchange__, __perms_publicas__),
(materia.Autoria, __base__), (materia.Anexada, __base__, __perms_publicas__),
(materia.Autoria, __base__, __perms_publicas__),
(materia.Proposicao, ['detail_proposicao_enviada', (materia.Proposicao, ['detail_proposicao_enviada',
'detail_proposicao_devolvida', 'detail_proposicao_devolvida',
'detail_proposicao_incorporada']), 'detail_proposicao_incorporada'], set()), # TODO: tratar em sapl.api questão de que proposições incorporadas serem públicas
(compilacao.TextoArticulado, ['view_restricted_textoarticulado']) (compilacao.TextoArticulado, [
'view_restricted_textoarticulado'], __perms_publicas__)
] ]
} }
rules_group_comissoes = { rules_group_comissoes = {
'group': SAPL_GROUP_COMISSOES, 'group': SAPL_GROUP_COMISSOES,
'rules': [ 'rules': [
(comissoes.Comissao, __base__), (comissoes.Comissao, __base__, __perms_publicas__),
(comissoes.Composicao, __base__), (comissoes.Composicao, __base__, __perms_publicas__),
(comissoes.Participacao, __base__), (comissoes.Participacao, __base__, __perms_publicas__),
(materia.Relatoria, __base__), (materia.Relatoria, __base__, __perms_publicas__),
(comissoes.Reuniao, __base__), (comissoes.Reuniao, __base__, __perms_publicas__),
(comissoes.DocumentoAcessorio, __base__), (comissoes.DocumentoAcessorio, __base__, __perms_publicas__),
] ]
} }
rules_group_materia = { rules_group_materia = {
'group': SAPL_GROUP_MATERIA, 'group': SAPL_GROUP_MATERIA,
'rules': [ 'rules': [
(materia.Anexada, __base__), (materia.Anexada, __base__, __perms_publicas__),
(materia.Autoria, __base__), (materia.Autoria, __base__, __perms_publicas__),
(materia.DespachoInicial, __base__), (materia.DespachoInicial, __base__, __perms_publicas__),
(materia.DocumentoAcessorio, __base__), (materia.DocumentoAcessorio, __base__, __perms_publicas__),
(materia.MateriaLegislativa, __base__ + ['can_access_impressos']), (materia.MateriaLegislativa, __base__ +
(materia.Numeracao, __base__), ['can_access_impressos'], __perms_publicas__),
(materia.Tramitacao, __base__), (materia.Numeracao, __base__, __perms_publicas__),
(norma.LegislacaoCitada, __base__), (materia.Tramitacao, __base__, __perms_publicas__),
(norma.AutoriaNorma, __base__), (norma.LegislacaoCitada, __base__, __perms_publicas__),
(norma.AutoriaNorma, __base__, __perms_publicas__),
(compilacao.Dispositivo, __base__ + [ (compilacao.Dispositivo, __base__ + [
'change_dispositivo_edicao_dinamica', 'change_dispositivo_edicao_dinamica',
@ -128,70 +135,70 @@ rules_group_materia = {
# uma matéria original. # uma matéria original.
# Fazer esse registro de compilação ofereceria # Fazer esse registro de compilação ofereceria
# um autografo eletrônico pronto para ser convertido em Norma. # um autografo eletrônico pronto para ser convertido em Norma.
]) ], __perms_publicas__)
] ]
} }
rules_group_norma = { rules_group_norma = {
'group': SAPL_GROUP_NORMA, 'group': SAPL_GROUP_NORMA,
'rules': [ 'rules': [
(norma.NormaJuridica, __base__), (norma.NormaJuridica, __base__, __perms_publicas__),
(norma.NormaRelacionada, __base__), (norma.NormaRelacionada, __base__, __perms_publicas__),
(norma.AnexoNormaJuridica, __base__), (norma.AnexoNormaJuridica, __base__, __perms_publicas__),
(norma.AutoriaNorma, __base__), (norma.AutoriaNorma, __base__, __perms_publicas__),
(norma.NormaEstatisticas, __base__), (norma.NormaEstatisticas, __base__, __perms_publicas__),
# Publicacao está com permissão apenas para norma e não para matéria # Publicacao está com permissão apenas para norma e não para matéria
# e proposições apenas por análise do contexto, não é uma limitação # e proposições apenas por análise do contexto, não é uma limitação
# da ferramenta. # da ferramenta.
(compilacao.Publicacao, __base__), (compilacao.Publicacao, __base__, __perms_publicas__),
(compilacao.Vide, __base__), (compilacao.Vide, __base__, __perms_publicas__),
(compilacao.Nota, __base__), (compilacao.Nota, __base__, __perms_publicas__),
(compilacao.Dispositivo, __base__ + [ (compilacao.Dispositivo, __base__ + [
'view_dispositivo_notificacoes', 'view_dispositivo_notificacoes',
'change_dispositivo_edicao_dinamica', 'change_dispositivo_edicao_dinamica',
'change_dispositivo_edicao_avancada', 'change_dispositivo_edicao_avancada',
'change_dispositivo_registros_compilacao', 'change_dispositivo_registros_compilacao',
'change_dispositivo_de_vigencia_global' 'change_dispositivo_de_vigencia_global'
]) ], __perms_publicas__)
] ]
} }
rules_group_sessao = { rules_group_sessao = {
'group': SAPL_GROUP_SESSAO, 'group': SAPL_GROUP_SESSAO,
'rules': [ 'rules': [
(sessao.SessaoPlenaria, __base__), (sessao.SessaoPlenaria, __base__, __perms_publicas__),
(sessao.SessaoPlenariaPresenca, __base__), (sessao.SessaoPlenariaPresenca, __base__, __perms_publicas__),
(sessao.ExpedienteMateria, __base__), (sessao.ExpedienteMateria, __base__, __perms_publicas__),
(sessao.OcorrenciaSessao, __base__), (sessao.OcorrenciaSessao, __base__, __perms_publicas__),
(sessao.IntegranteMesa, __base__), (sessao.IntegranteMesa, __base__, __perms_publicas__),
(sessao.ExpedienteSessao, __base__), (sessao.ExpedienteSessao, __base__, __perms_publicas__),
(sessao.Orador, __base__), (sessao.Orador, __base__, __perms_publicas__),
(sessao.OradorExpediente, __base__), (sessao.OradorExpediente, __base__, __perms_publicas__),
(sessao.OrdemDia, __base__), (sessao.OrdemDia, __base__, __perms_publicas__),
(sessao.PresencaOrdemDia, __base__), (sessao.PresencaOrdemDia, __base__, __perms_publicas__),
(sessao.RegistroVotacao, __base__), (sessao.RegistroVotacao, __base__, __perms_publicas__),
(sessao.VotoParlamentar, __base__), (sessao.VotoParlamentar, __base__, __perms_publicas__),
(sessao.JustificativaAusencia, __base__), (sessao.JustificativaAusencia, __base__, __perms_publicas__),
(sessao.RetiradaPauta, __base__) (sessao.RetiradaPauta, __base__, __perms_publicas__),
] ]
} }
rules_group_painel = { rules_group_painel = {
'group': SAPL_GROUP_PAINEL, 'group': SAPL_GROUP_PAINEL,
'rules': [ 'rules': [
(painel.Painel, __base__), (painel.Painel, __base__, __perms_publicas__),
(painel.Cronometro, __base__), (painel.Cronometro, __base__, __perms_publicas__),
] ]
} }
rules_group_autor = { rules_group_autor = {
'group': SAPL_GROUP_AUTOR, 'group': SAPL_GROUP_AUTOR,
'rules': [ 'rules': [
(materia.Proposicao, __base__), (materia.Proposicao, __base__, set()),
(compilacao.Dispositivo, __base__ + [ (compilacao.Dispositivo, __base__ + [
'change_your_dispositivo_edicao_dinamica', 'change_your_dispositivo_edicao_dinamica',
]) ], __perms_publicas__)
] ]
} }
@ -203,7 +210,7 @@ rules_group_parlamentar = {
rules_group_votante = { rules_group_votante = {
'group': SAPL_GROUP_VOTANTE, 'group': SAPL_GROUP_VOTANTE,
'rules': [ 'rules': [
(parlamentares.Votante, ['can_vote']) (parlamentares.Votante, ['can_vote'], set())
] ]
} }
@ -213,89 +220,94 @@ rules_group_geral = {
(base.AppConfig, __base__ + [ (base.AppConfig, __base__ + [
'menu_sistemas', 'menu_sistemas',
'view_tabelas_auxiliares' 'view_tabelas_auxiliares'
]), ], set()),
(base.CasaLegislativa, __listdetailchange__ + [RP_ADD]), (base.CasaLegislativa, __listdetailchange__ +
(base.TipoAutor, __base__), [RP_ADD], __perms_publicas__),
(base.Autor, __base__), (base.TipoAutor, __base__, __perms_publicas__),
(base.Autor, __base__, __perms_publicas__),
(protocoloadm.StatusTramitacaoAdministrativo, __base__),
(protocoloadm.TipoDocumentoAdministrativo, __base__), (protocoloadm.StatusTramitacaoAdministrativo, __base__, set()),
(protocoloadm.TipoDocumentoAdministrativo, __base__, set()),
(comissoes.CargoComissao, __base__),
(comissoes.TipoComissao, __base__), (comissoes.CargoComissao, __base__, __perms_publicas__),
(comissoes.Periodo, __base__), (comissoes.TipoComissao, __base__, __perms_publicas__),
(comissoes.Periodo, __base__, __perms_publicas__),
(materia.AssuntoMateria, __base__), # não há implementação
(materia.MateriaAssunto, __base__), # não há implementação (materia.AssuntoMateria, __base__,
(materia.MateriaLegislativa, ['can_access_impressos']), __perms_publicas__), # não há implementação
(materia.TipoProposicao, __base__), (materia.MateriaAssunto, __base__,
(materia.TipoMateriaLegislativa, __base__), __perms_publicas__), # não há implementação
(materia.RegimeTramitacao, __base__), (materia.MateriaLegislativa, [
(materia.Origem, __base__), 'can_access_impressos'], __perms_publicas__),
(materia.TipoDocumento, __base__), (materia.TipoProposicao, __base__, __perms_publicas__),
(materia.Orgao, __base__), (materia.TipoMateriaLegislativa, __base__, __perms_publicas__),
(materia.TipoFimRelatoria, __base__), (materia.RegimeTramitacao, __base__, __perms_publicas__),
(materia.Parecer, __base__), (materia.Origem, __base__, __perms_publicas__),
(materia.StatusTramitacao, __base__), (materia.TipoDocumento, __base__, __perms_publicas__),
(materia.UnidadeTramitacao, __base__), (materia.Orgao, __base__, __perms_publicas__),
(materia.TipoFimRelatoria, __base__, __perms_publicas__),
(norma.AssuntoNorma, __base__), (materia.Parecer, __base__, __perms_publicas__),
(norma.TipoNormaJuridica, __base__), (materia.StatusTramitacao, __base__, __perms_publicas__),
(norma.TipoVinculoNormaJuridica, __base__), (materia.UnidadeTramitacao, __base__, __perms_publicas__),
(norma.NormaEstatisticas, __base__),
(parlamentares.Legislatura, __base__), (norma.AssuntoNorma, __base__, __perms_publicas__),
(parlamentares.SessaoLegislativa, __base__), (norma.TipoNormaJuridica, __base__, __perms_publicas__),
(parlamentares.Coligacao, __base__), (norma.TipoVinculoNormaJuridica, __base__, __perms_publicas__),
(parlamentares.ComposicaoColigacao, __base__), (norma.NormaEstatisticas, __base__, __perms_publicas__),
(parlamentares.Partido, __base__),
(parlamentares.NivelInstrucao, __base__), (parlamentares.Legislatura, __base__, __perms_publicas__),
(parlamentares.SituacaoMilitar, __base__), (parlamentares.SessaoLegislativa, __base__, __perms_publicas__),
(parlamentares.Parlamentar, __base__), (parlamentares.Coligacao, __base__, __perms_publicas__),
(parlamentares.TipoDependente, __base__), (parlamentares.ComposicaoColigacao, __base__, __perms_publicas__),
(parlamentares.Dependente, __base__), (parlamentares.Partido, __base__, __perms_publicas__),
(parlamentares.Filiacao, __base__), (parlamentares.NivelInstrucao, __base__, __perms_publicas__),
(parlamentares.TipoAfastamento, __base__), (parlamentares.SituacaoMilitar, __base__, __perms_publicas__),
(parlamentares.Mandato, __base__), (parlamentares.Parlamentar, __base__, __perms_publicas__),
(parlamentares.CargoMesa, __base__), (parlamentares.TipoDependente, __base__, __perms_publicas__),
(parlamentares.ComposicaoMesa, __base__), (parlamentares.Dependente, __base__, __perms_publicas__),
(parlamentares.Frente, __base__), (parlamentares.Filiacao, __base__, __perms_publicas__),
(parlamentares.Votante, __base__), (parlamentares.TipoAfastamento, __base__, __perms_publicas__),
(parlamentares.Mandato, __base__, __perms_publicas__),
(sessao.CargoBancada, __base__), (parlamentares.CargoMesa, __base__, __perms_publicas__),
(sessao.Bancada, __base__), (parlamentares.ComposicaoMesa, __base__, __perms_publicas__),
(sessao.TipoSessaoPlenaria, __base__), (parlamentares.Frente, __base__, __perms_publicas__),
(sessao.TipoResultadoVotacao, __base__), (parlamentares.Votante, __base__, __perms_publicas__),
(sessao.TipoExpediente, __base__),
(sessao.TipoJustificativa, __base__), (sessao.CargoBancada, __base__, __perms_publicas__),
(sessao.JustificativaAusencia, __base__), (sessao.Bancada, __base__, __perms_publicas__),
(sessao.Bloco, __base__), (sessao.TipoSessaoPlenaria, __base__, __perms_publicas__),
(sessao.ResumoOrdenacao, __base__), (sessao.TipoResultadoVotacao, __base__, __perms_publicas__),
(sessao.TipoRetiradaPauta, __base__), (sessao.TipoExpediente, __base__, __perms_publicas__),
(sessao.TipoJustificativa, __base__, __perms_publicas__),
(lexml.LexmlProvedor, __base__), (sessao.JustificativaAusencia, __base__, __perms_publicas__),
(lexml.LexmlPublicador, __base__), (sessao.Bloco, __base__, __perms_publicas__),
(sessao.ResumoOrdenacao, __base__, __perms_publicas__),
(compilacao.VeiculoPublicacao, __base__), (sessao.TipoRetiradaPauta, __base__, __perms_publicas__),
(compilacao.TipoTextoArticulado, __base__),
(compilacao.TipoNota, __base__), (lexml.LexmlProvedor, __base__, set()),
(compilacao.TipoVide, __base__), (lexml.LexmlPublicador, __base__, set()),
(compilacao.TipoPublicacao, __base__),
(compilacao.VeiculoPublicacao, __base__, __perms_publicas__),
(compilacao.TipoTextoArticulado, __base__, __perms_publicas__),
(compilacao.TipoNota, __base__, __perms_publicas__),
(compilacao.TipoVide, __base__, __perms_publicas__),
(compilacao.TipoPublicacao, __base__, __perms_publicas__),
# este model é um espelho do model integrado e sua edição pode # este model é um espelho do model integrado e sua edição pode
# confundir Autores, operadores de matéria e/ou norma. # confundir Autores, operadores de matéria e/ou norma.
# Por isso está adicionado apenas para o operador geral # Por isso está adicionado apenas para o operador geral
(compilacao.TextoArticulado, (compilacao.TextoArticulado,
__base__ + ['lock_unlock_textoarticulado']), __base__ + ['lock_unlock_textoarticulado'], set()),
# estes tres models são complexos e a principio apenas o admin tem perm # estes tres models são complexos e a principio apenas o admin tem perm
(compilacao.TipoDispositivo, []), (compilacao.TipoDispositivo, [], set()),
(compilacao.TipoDispositivoRelationship, []), (compilacao.TipoDispositivoRelationship, [], set()),
(compilacao.PerfilEstruturalTextoArticulado, []), (compilacao.PerfilEstruturalTextoArticulado, [], set()),
(audiencia.AudienciaPublica, __base__), (audiencia.AudienciaPublica, __base__, __perms_publicas__),
(audiencia.TipoAudienciaPublica, __base__), (audiencia.TipoAudienciaPublica, __base__, __perms_publicas__),
@ -309,8 +321,8 @@ rules_group_geral = {
rules_group_anonymous = { rules_group_anonymous = {
'group': SAPL_GROUP_ANONYMOUS, 'group': SAPL_GROUP_ANONYMOUS,
'rules': [ 'rules': [
(materia.AcompanhamentoMateria, [RP_ADD, RP_DELETE]), (materia.AcompanhamentoMateria, [RP_ADD, RP_DELETE], set()),
(protocoloadm.AcompanhamentoDocumento, [RP_ADD, RP_DELETE]), (protocoloadm.AcompanhamentoDocumento, [RP_ADD, RP_DELETE], set()),
] ]
} }
@ -348,3 +360,23 @@ rules_patterns = [
rules_group_anonymous, # anotação para validação do teste de rules rules_group_anonymous, # anotação para validação do teste de rules
rules_group_login_social # TODO não implementado rules_group_login_social # TODO não implementado
] ]
rules_patterns_public = {}
def _get_registration_key(model):
return '%s:%s' % (model._meta.app_label, model._meta.model_name)
for rules_group in rules_patterns:
for rules in rules_group['rules']:
key = _get_registration_key(rules[0])
if key not in rules_patterns_public:
rules_patterns_public[key] = set()
r = set(map(lambda x, m=rules[0]: '{}{}{}'.format(
m._meta.app_label,
x,
m._meta.model_name), rules[2]))
rules_patterns_public[key] = rules_patterns_public[key] | r

6
sapl/settings.py

@ -157,9 +157,11 @@ REST_FRAMEWORK = {
"DEFAULT_PARSER_CLASSES": ( "DEFAULT_PARSER_CLASSES": (
"rest_framework.parsers.JSONParser", "rest_framework.parsers.JSONParser",
), ),
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
),
"DEFAULT_PERMISSION_CLASSES": ( "DEFAULT_PERMISSION_CLASSES": (
"rest_framework.permissions.IsAuthenticated", "sapl.api.permissions.SaplModelPermissions",
"sapl.api.permissions.DjangoModelPermissions",
), ),
"DEFAULT_AUTHENTICATION_CLASSES": ( "DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework.authentication.SessionAuthentication", "rest_framework.authentication.SessionAuthentication",

Loading…
Cancel
Save