mirror of https://github.com/interlegis/sapl.git
Browse Source
- Grupos foram definidos em sapl.rules.__init__.py - as Regras que relac grupos e permissões estão sapl.rules.map_rules.py - em sapl.rules.apps.py estão dois signals post_migrate que indicam ao django tarefas a serem feias após a migração, são elas: - adicionar duas rules além das padrões do django, list e detail. - carregar as rules_patterns definidas em map_rules e criar os grupos referentes e, em modo debug criar alguns usuários relevantes. - adiciona testes de validação das rules que: - testa se todos os grupos definidos estão em rules_patterns - testa se todos os models do SAPL estão na rules_patterns - testa se todas as permissões adicionadas em rules_patterns foram definidas nas classes Meta de cada modelpull/781/head
LeandroRoberto
8 years ago
14 changed files with 306 additions and 406 deletions
@ -0,0 +1,19 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.9.7 on 2016-10-27 13:23 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('compilacao', '0058_auto_20161008_0143'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AlterModelOptions( |
|||
name='dispositivo', |
|||
options={'ordering': ['ta', 'ordem'], 'permissions': (('change_dispositivo_edicao_dinamica', 'Permissão de edição de dispositivos originais via editor dinâmico.'), ('change_dispositivo_edicao_avancada', 'Permissão de edição de dispositivos originais via formulários de edição avançada.'), ('change_dispositivo_registros_compilacao', 'Permissão de registro de compilação via editor dinâmico.'), ('change_dispositivo_notificacoes', 'Permissão de acesso às notificações de pendências.')), 'verbose_name': 'Dispositivo', 'verbose_name_plural': 'Dispositivos'}, |
|||
), |
|||
] |
@ -0,0 +1,38 @@ |
|||
from django.utils.translation import ugettext_lazy as _ |
|||
|
|||
default_app_config = 'sapl.rules.apps.AppConfig' |
|||
|
|||
|
|||
SAPL_GROUP_ADMINISTRATIVO = _("Operador Administrativo") |
|||
SAPL_GROUP_PROTOCOLO = _("Operador de Protocolo Administrativo") |
|||
SAPL_GROUP_COMISSOES = _("Operador de Comissões") |
|||
SAPL_GROUP_MATERIA = _("Operador de Matéria") |
|||
SAPL_GROUP_NORMA = _("Operador de Norma Jurídica") |
|||
SAPL_GROUP_SESSAO = _("Operador de Sessão Plenária") |
|||
SAPL_GROUP_PAINEL = _("Operador de Painel Eletrônico") |
|||
SAPL_GROUP_GERAL = _("Operador Geral") |
|||
SAPL_GROUP_AUTOR = _("Autor") |
|||
SAPL_GROUP_PARLAMENTAR = _("Parlamentar") |
|||
|
|||
# TODO - funcionalidade ainda não existe mas está aqui para efeito de anotação |
|||
SAPL_GROUP_LOGIN_SOCIAL = _("Usuários com Login Social") |
|||
|
|||
# ANONYMOUS não é um grupo mas é uma variável usadas nas rules para anotar |
|||
# explicitamente models que podem ter ação de usuários anônimos |
|||
# como por exemplo AcompanhamentoMateria |
|||
SAPL_GROUP_ANONYMOUS = '' |
|||
|
|||
SAPL_GROUPS = [ |
|||
SAPL_GROUP_ADMINISTRATIVO, |
|||
SAPL_GROUP_PROTOCOLO, |
|||
SAPL_GROUP_COMISSOES, |
|||
SAPL_GROUP_MATERIA, |
|||
SAPL_GROUP_NORMA, |
|||
SAPL_GROUP_SESSAO, |
|||
SAPL_GROUP_PAINEL, |
|||
SAPL_GROUP_GERAL, |
|||
SAPL_GROUP_AUTOR, |
|||
SAPL_GROUP_PARLAMENTAR, |
|||
SAPL_GROUP_LOGIN_SOCIAL, |
|||
SAPL_GROUP_ANONYMOUS, |
|||
] |
@ -0,0 +1,231 @@ |
|||
from builtins import LookupError |
|||
|
|||
from django.apps import apps |
|||
from django.conf import settings |
|||
from django.contrib.auth import get_user_model |
|||
from django.contrib.auth.management import _get_all_permissions |
|||
from django.core import exceptions |
|||
from django.db import router, models |
|||
from django.db.models.signals import post_migrate |
|||
from django.db.utils import DEFAULT_DB_ALIAS |
|||
from django.utils.translation import string_concat |
|||
from django.utils.translation import ugettext_lazy as _ |
|||
import django |
|||
from sapl.rules import SAPL_GROUP_ADMINISTRATIVO, SAPL_GROUP_COMISSOES,\ |
|||
SAPL_GROUP_GERAL, SAPL_GROUP_MATERIA, SAPL_GROUP_NORMA, SAPL_GROUP_PAINEL,\ |
|||
SAPL_GROUP_PROTOCOLO, SAPL_GROUP_SESSAO |
|||
|
|||
|
|||
class AppConfig(django.apps.AppConfig): |
|||
name = 'sapl.rules' |
|||
label = 'rules' |
|||
verbose_name = _('Regras de Acesso') |
|||
|
|||
|
|||
def create_proxy_permissions( |
|||
app_config, verbosity=2, interactive=True, |
|||
using=DEFAULT_DB_ALIAS, **kwargs): |
|||
if not app_config.models_module: |
|||
return |
|||
|
|||
# print(app_config) |
|||
|
|||
try: |
|||
Permission = apps.get_model('auth', 'Permission') |
|||
except LookupError: |
|||
return |
|||
|
|||
if not router.allow_migrate_model(using, Permission): |
|||
return |
|||
|
|||
from django.contrib.contenttypes.models import ContentType |
|||
|
|||
permission_name_max_length = Permission._meta.get_field('name').max_length |
|||
|
|||
# This will hold the permissions we're looking for as |
|||
# (content_type, (codename, name)) |
|||
searched_perms = list() |
|||
# The codenames and ctypes that should exist. |
|||
ctypes = set() |
|||
for klass in list(app_config.get_models()): |
|||
opts = klass._meta |
|||
permissions = ( |
|||
("list_" + opts.model_name, |
|||
string_concat( |
|||
_('Visualizaçao da lista de'), ' ', |
|||
opts.verbose_name_plural)), |
|||
("detail_" + opts.model_name, |
|||
string_concat( |
|||
_('Visualização dos detalhes de'), ' ', |
|||
opts.verbose_name_plural)), |
|||
) |
|||
opts.permissions = tuple( |
|||
set(list(permissions) + list(opts.permissions))) |
|||
|
|||
if opts.proxy: |
|||
# Force looking up the content types in the current database |
|||
# before creating foreign keys to them. |
|||
app_label, model = opts.app_label, opts.model_name |
|||
|
|||
try: |
|||
ctype = ContentType.objects.db_manager( |
|||
using).get_by_natural_key(app_label, model) |
|||
except: |
|||
ctype = ContentType.objects.db_manager( |
|||
using).create(app_label=app_label, model=model) |
|||
else: |
|||
ctype = ContentType.objects.db_manager(using).get_for_model(klass) |
|||
|
|||
ctypes.add(ctype) |
|||
for perm in _get_all_permissions(klass._meta, ctype): |
|||
searched_perms.append((ctype, perm)) |
|||
|
|||
# Find all the Permissions that have a content_type for a model we're |
|||
# looking for. We don't need to check for codenames since we already have |
|||
# a list of the ones we're going to create. |
|||
all_perms = set(Permission.objects.using(using).filter( |
|||
content_type__in=ctypes, |
|||
).values_list( |
|||
"content_type", "codename" |
|||
)) |
|||
|
|||
perms = [ |
|||
Permission(codename=codename, name=name, content_type=ct) |
|||
for ct, (codename, name) in searched_perms |
|||
if (ct.pk, codename) not in all_perms |
|||
] |
|||
# Validate the permissions before bulk_creation to avoid cryptic database |
|||
# error when the name is longer than 255 characters |
|||
for perm in perms: |
|||
if len(perm.name) > permission_name_max_length: |
|||
raise exceptions.ValidationError( |
|||
'The permission name %s of %s.%s ' |
|||
'is longer than %s characters' % ( |
|||
perm.name, |
|||
perm.content_type.app_label, |
|||
perm.content_type.model, |
|||
permission_name_max_length, |
|||
) |
|||
) |
|||
Permission.objects.using(using).bulk_create(perms) |
|||
if verbosity >= 2: |
|||
for perm in perms: |
|||
print("Adding permission '%s'" % perm) |
|||
|
|||
|
|||
def update_groups(app_config, verbosity=2, interactive=True, |
|||
using=DEFAULT_DB_ALIAS, **kwargs): |
|||
|
|||
if app_config != AppConfig and not isinstance(app_config, AppConfig): |
|||
return |
|||
|
|||
from sapl.rules.map_rules import rules_patterns |
|||
from django.contrib.auth.models import Group, Permission |
|||
from django.contrib.contenttypes.models import ContentType |
|||
|
|||
class Rules: |
|||
|
|||
def __init__(self, rules_patterns): |
|||
self.rules_patterns = rules_patterns |
|||
|
|||
def associar(self, g, model, tipo): |
|||
for t in tipo: |
|||
content_type = ContentType.objects.get_by_natural_key( |
|||
app_label=model._meta.app_label, model=model._meta.model_name) |
|||
|
|||
codename = (t[1:] + model._meta.model_name)\ |
|||
if t[0] == '.' and t[-1] == '_' else t |
|||
|
|||
p = Permission.objects.get( |
|||
content_type=content_type, |
|||
codename=codename) |
|||
g.permissions.add(p) |
|||
g.save() |
|||
|
|||
def _config_group(self, group_name, rules_list): |
|||
if not group_name: |
|||
return |
|||
|
|||
g = Group.objects.get_or_create(name=group_name) |
|||
if not isinstance(g, Group): |
|||
g = g[0] |
|||
g.permissions.clear() |
|||
|
|||
try: |
|||
|
|||
print(' ', group_name) |
|||
for model, perms in rules_list: |
|||
self.associar(g, model, perms) |
|||
except Exception as e: |
|||
print(group_name, e) |
|||
|
|||
if settings.DEBUG: |
|||
user = '' |
|||
if group_name == SAPL_GROUP_ADMINISTRATIVO: |
|||
user = 'operador_administrativo' |
|||
elif group_name == SAPL_GROUP_PROTOCOLO: |
|||
user = 'operador_protocoloadm' |
|||
elif group_name == SAPL_GROUP_COMISSOES: |
|||
user = 'operador_comissoes' |
|||
elif group_name == SAPL_GROUP_MATERIA: |
|||
user = 'operador_materia' |
|||
elif group_name == SAPL_GROUP_NORMA: |
|||
user = 'operador_norma' |
|||
elif group_name == SAPL_GROUP_SESSAO: |
|||
user = 'operador_sessao' |
|||
elif group_name == SAPL_GROUP_PAINEL: |
|||
user = 'operador_painel' |
|||
elif group_name == SAPL_GROUP_GERAL: |
|||
user = 'operador_geral' |
|||
|
|||
if user: |
|||
self.cria_usuario(user, g) |
|||
|
|||
def groups_add_user(self, user, groups_name): |
|||
if not isinstance(groups_name, list): |
|||
groups_name = [groups_name, ] |
|||
for group_name in groups_name: |
|||
if not group_name or user.groups.filter(name=group_name).exists(): |
|||
continue |
|||
g = Group.objects.get_or_create(name=group_name)[0] |
|||
user.groups.add(g) |
|||
|
|||
def groups_remove_user(self, user, groups_name): |
|||
if not isinstance(groups_name, list): |
|||
groups_name = [groups_name, ] |
|||
for group_name in groups_name: |
|||
if not group_name or not user.groups.filter( |
|||
name=group_name).exists(): |
|||
continue |
|||
g = Group.objects.get_or_create(name=group_name)[0] |
|||
user.groups.remove(g) |
|||
|
|||
def cria_usuario(self, nome, grupo): |
|||
nome_usuario = nome |
|||
usuario = get_user_model().objects.get_or_create( |
|||
username=nome_usuario)[0] |
|||
usuario.set_password('interlegis') |
|||
usuario.save() |
|||
grupo.user_set.add(usuario) |
|||
|
|||
def update_groups(self): |
|||
print('') |
|||
print(string_concat('\033[93m\033[1m', |
|||
_('Atualizando grupos:'), |
|||
'\033[0m')) |
|||
for rules_group in self.rules_patterns: |
|||
group_name = rules_group['group'] |
|||
rules_list = rules_group['rules'] |
|||
self._config_group(group_name, rules_list) |
|||
|
|||
rules = Rules(rules_patterns) |
|||
rules.update_groups() |
|||
|
|||
|
|||
models.signals.post_migrate.connect( |
|||
receiver=update_groups) |
|||
|
|||
|
|||
models.signals.post_migrate.connect( |
|||
receiver=create_proxy_permissions, |
|||
dispatch_uid="django.contrib.auth.management.create_permissions") |
@ -1,135 +0,0 @@ |
|||
import os |
|||
|
|||
import django |
|||
|
|||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sapl.settings") |
|||
django.setup() |
|||
|
|||
if True: |
|||
from django.apps import apps |
|||
from django.contrib.auth import get_user_model |
|||
from django.contrib.auth.models import Group, Permission |
|||
from django.contrib.contenttypes.models import ContentType |
|||
|
|||
|
|||
class InicializaGruposAutorizacoes(): |
|||
|
|||
def cria_ou_reseta_grupo(self, nome): |
|||
grupo = Group.objects.get_or_create(name=nome)[0] |
|||
for p in list(grupo.permissions.all()): |
|||
grupo.permissions.remove(p) |
|||
return grupo |
|||
|
|||
def cria_usuario(self, nome, grupo): |
|||
nome_usuario = nome |
|||
usuario = get_user_model().objects.get_or_create( |
|||
username=nome_usuario)[0] |
|||
usuario.set_password('interlegis') |
|||
usuario.save() |
|||
grupo.user_set.add(usuario) |
|||
|
|||
def cria_grupos_permissoes(self): |
|||
|
|||
nomes_apps = ['base', 'parlamentares', 'comissoes', |
|||
'materia', 'norma', 'sessao', 'painel'] |
|||
|
|||
permissoes = {app: list(Permission.objects.filter( |
|||
content_type__in=ContentType.objects.filter(app_label=app))) |
|||
for app in nomes_apps} |
|||
|
|||
# permissoes específicas para protocolo e documento administrativo |
|||
cts = ContentType.objects.filter(app_label='protocoloadm') |
|||
|
|||
# documento administrativo |
|||
permissoes['documento_administrativo'] = list( |
|||
Permission.objects.filter(content_type__in=cts)) |
|||
nome_grupo = 'Operador Administrativo' |
|||
grupo = self.cria_ou_reseta_grupo(nome_grupo) |
|||
for p in permissoes['documento_administrativo']: |
|||
grupo.permissions.add(p) |
|||
|
|||
nome_usuario = 'operador_administrativo' |
|||
self.cria_usuario(nome_usuario, grupo) |
|||
|
|||
# prolocolo administrativo |
|||
cts = cts.exclude(model__icontains='tramitacao').exclude( |
|||
model__icontains='documentoadministrativo') |
|||
permissoes['protocoloadm'] = list( |
|||
Permission.objects.filter(content_type__in=cts)) |
|||
nome_grupo = 'Operador de Protocolo Administrativo' |
|||
grupo = self.cria_ou_reseta_grupo(nome_grupo) |
|||
for p in permissoes['protocoloadm']: |
|||
grupo.permissions.add(p) |
|||
|
|||
nome_usuario = 'operador_protocoloadm' |
|||
self.cria_usuario(nome_usuario, grupo) |
|||
|
|||
# permissoes do base |
|||
cts = ContentType.objects.filter(app_label='base') |
|||
permissoes['base'] = list( |
|||
Permission.objects.filter(content_type__in=cts)) |
|||
|
|||
for nome_app in nomes_apps: |
|||
|
|||
if nome_app not in {'base', 'parlamentares'}: |
|||
# Elimina casos especificos |
|||
|
|||
# Cria Grupo |
|||
nome_grupo = 'Operador de %s' % apps.get_app_config( |
|||
nome_app).verbose_name |
|||
grupo = self.cria_ou_reseta_grupo(nome_grupo) |
|||
|
|||
# Elimina o acesso a proposicoes pelo Operador de Matérias |
|||
if nome_app == 'materia': |
|||
cts = ContentType.objects.filter( |
|||
app_label='materia').exclude(model='proposicao') |
|||
permissoes['materia'] = list( |
|||
Permission.objects.filter(content_type__in=cts)) |
|||
|
|||
# Configura as permissoes |
|||
for p in permissoes[nome_app]: |
|||
grupo.permissions.add(p) |
|||
|
|||
# Cria o Usuario |
|||
nome_usuario = 'operador_%s' % nome_app |
|||
usuario = get_user_model().objects.get_or_create( |
|||
username=nome_usuario)[0] |
|||
usuario.set_password('interlegis') |
|||
usuario.save() |
|||
grupo.user_set.add(usuario) |
|||
|
|||
# Operador Geral |
|||
grupo_geral = self.cria_ou_reseta_grupo('Operador Geral') |
|||
for lista in permissoes.values(): |
|||
for p in lista: |
|||
grupo_geral.permissions.add(p) |
|||
|
|||
nome_usuario = 'operador_geral' |
|||
self.cria_usuario(nome_usuario, grupo_geral) |
|||
|
|||
# Autor |
|||
grupo = self.cria_ou_reseta_grupo('Autor') |
|||
|
|||
list(map(lambda permissao: grupo.permissions.add(permissao), |
|||
list(Permission.objects.filter( |
|||
content_type=ContentType.objects.get_by_natural_key( |
|||
app_label='materia', model='proposicao'))))) |
|||
|
|||
""" |
|||
Mesmo para teste, um usuário com perfil Autor criado via /admin |
|||
não deverá ser criado pois esse é um papel do operador_geral fazer |
|||
nas tabelas auxiliares. |
|||
A tentativa de acesso a qualquer container (hoje apenas proposições) |
|||
do SAPL de Usuários com perfil Autor mas sem um Autor cadastrado |
|||
nas tabelas auxiliares será negado e notificado via front-end. |
|||
""" |
|||
# nome_usuario = 'operador_autor' |
|||
# self.cria_usuario(nome_usuario, grupo) |
|||
|
|||
def __call__(self): |
|||
self.cria_grupos_permissoes() |
|||
|
|||
|
|||
cria_grupos_permissoes = InicializaGruposAutorizacoes() |
|||
if __name__ == '__main__': |
|||
cria_grupos_permissoes.cria_grupos_permissoes() |
@ -1,116 +0,0 @@ |
|||
import pytest |
|||
from django.apps import apps |
|||
from django.contrib.auth.management import _get_all_permissions |
|||
from django.contrib.auth.models import Group, Permission |
|||
from django.contrib.contenttypes.models import ContentType |
|||
from django.utils.encoding import force_text |
|||
from django.utils.translation import ugettext_lazy as _ |
|||
from django.utils.translation import string_concat |
|||
|
|||
from inicializa_grupos_autorizacoes import cria_grupos_permissoes |
|||
|
|||
pytestmark = pytest.mark.django_db |
|||
|
|||
apps_com_permissao_padrao = [ |
|||
'comissoes', 'norma', 'sessao', 'painel'] |
|||
|
|||
|
|||
def create_perms_post_migrate(app): |
|||
|
|||
searched_perms = list() |
|||
# The codenames and ctypes that should exist. |
|||
ctypes = set() |
|||
|
|||
for klass in list(app.get_models()): |
|||
opts = klass._meta |
|||
permissions = ( |
|||
("list_" + opts.model_name, |
|||
string_concat( |
|||
_('Visualizaçao da lista de'), ' ', |
|||
opts.verbose_name_plural)), |
|||
("detail_" + opts.model_name, |
|||
string_concat( |
|||
_('Visualização dos detalhes de'), ' ', |
|||
opts.verbose_name_plural)), |
|||
) |
|||
opts.permissions = tuple( |
|||
set(list(permissions) + list(opts.permissions))) |
|||
|
|||
if opts.proxy: |
|||
# Force looking up the content types in the current database |
|||
# before creating foreign keys to them. |
|||
app_label, model = opts.app_label, opts.model_name |
|||
|
|||
try: |
|||
ctype = ContentType.objects.get_by_natural_key( |
|||
app_label, model) |
|||
except: |
|||
ctype = ContentType.objects.create( |
|||
app_label=app_label, model=model) |
|||
else: |
|||
ctype = ContentType.objects.get_for_model(klass) |
|||
|
|||
ctypes.add(ctype) |
|||
for perm in _get_all_permissions(klass._meta, ctype): |
|||
searched_perms.append((ctype, perm)) |
|||
|
|||
all_perms = set(Permission.objects.filter( |
|||
content_type__in=ctypes, |
|||
).values_list( |
|||
"content_type", "codename" |
|||
)) |
|||
|
|||
perms = [ |
|||
Permission(codename=codename, name=name, content_type=ct) |
|||
for ct, (codename, name) in searched_perms |
|||
if (ct.pk, codename) not in all_perms |
|||
] |
|||
Permission.objects.bulk_create(perms) |
|||
|
|||
|
|||
@pytest.mark.parametrize('app_label', apps_com_permissao_padrao) |
|||
def test_grupo_padrao_tem_permissoes_sobre_todo_o_app(app_label): |
|||
|
|||
app = apps.get_app_config(app_label) |
|||
|
|||
create_perms_post_migrate(app) |
|||
|
|||
# código testado |
|||
cria_grupos_permissoes() |
|||
|
|||
def gerar_permissoes(app): |
|||
for model in app.get_models(): |
|||
for op in ['add', 'change', 'delete', ]: |
|||
yield model, 'Can %s %s' % (op, model._meta.verbose_name) |
|||
yield model, force_text(string_concat( |
|||
_('Visualizaçao da lista de'), ' ', |
|||
model._meta.verbose_name_plural)) |
|||
yield model, force_text(string_concat( |
|||
_('Visualização dos detalhes de'), |
|||
' ', |
|||
model._meta.verbose_name_plural)) |
|||
grupo = Group.objects.get(name='Operador de %s' % app.verbose_name) |
|||
esperado = set(gerar_permissoes(app)) |
|||
|
|||
real = set((p.content_type.model_class(), p.name) |
|||
for p in grupo.permissions.all()) |
|||
assert real == esperado |
|||
|
|||
|
|||
@pytest.mark.parametrize('app_label', apps_com_permissao_padrao) |
|||
def test_permissoes_extras_sao_apagadas(app_label): |
|||
|
|||
app = apps.get_app_config(app_label) |
|||
|
|||
# create_perms_post_migrate(app) |
|||
|
|||
grupo = Group.objects.create(name='Operador de %s' % app.verbose_name) |
|||
|
|||
permissao_errada = Permission.objects.create( |
|||
name='STUB', content_type=ContentType.objects.first()) |
|||
grupo.permissions.add(permissao_errada) |
|||
|
|||
# código testado |
|||
cria_grupos_permissoes() |
|||
|
|||
assert not grupo.permissions.filter(id=permissao_errada.id).exists() |
Loading…
Reference in new issue