|
| 1 | +Title: Solução (quase) definitiva para permissões em projetos Django |
| 2 | +Date: 2014-11-22 11:30 |
| 3 | +Tags: Django, django-global-permissions |
| 4 | +Category: Django |
| 5 | +Slug: solucao-quase-definitiva-para-permissoes-em-projetos-django |
| 6 | +Author: Eduardo Matos |
| 7 | +Email: eduardo.matos.silva@gmail.com |
| 8 | +Github: eduardo-matos |
| 9 | + |
| 10 | +De todas as tarefas que o Django se propõe a resolver, é possível que o módulo de permissões seja o mais gera dúvidas. Seu funcionamento é bastante simples, mas existe uma peça faltando no quebra-cabeça que o torna confuso para marinheiros de primeira viagem. |
| 11 | + |
| 12 | +## O que está faltando? |
| 13 | + |
| 14 | +Atualmente o Django suporta, de forma nativa, somente permissões baseadas em modelos. Então é possível atribuir ou remover a permissão criar/alterar/deletar um dado modelo. Essas permissões são criadas criadas automaticamente através da inspeção dos modelos usados na aplicação, bastando estar persente na tupla `INSTALLED_APPS`. |
| 15 | + |
| 16 | +O problema reside no fato de que, em geral, não nos interessa atribuir permissões a modelos, e sim criar permissões genéricas, como poder acessar uma página ou poder visualizar um item no menu por exemplo. Como este tipo de permissão não está atrelada a um modelo, em tese não é possível utilizá-la. |
| 17 | + |
| 18 | +## Qual a solução? |
| 19 | + |
| 20 | +Devido a ser um problema recorrente, publiquei um pacote no PyPI chamado [Django Global Permissions](https://pypi.python.org/pypi/django-global-permissions/0.1.0), que possibilita a criação de permissões genéricas, resolvendo o empecilho que mencionei anteriormente. |
| 21 | + |
| 22 | +Para utilizar esse pacote, basta instalá-lo e adicioná-lo à tupla `INSTALLED_APPS`. |
| 23 | + |
| 24 | +``` |
| 25 | +pip install django-global-permissions |
| 26 | +``` |
| 27 | + |
| 28 | +```python |
| 29 | +INSTALLED_APPS += ('global_permissions',) |
| 30 | +``` |
| 31 | + |
| 32 | +Caso você use o admin do Django, você pode acessar a seção do *Global Permissons*, e criar suas permissões genéricas informando `name` (descrição) e `codename`. O `codename` será utilizado sempre que for necessário verificar uma dada permissão. É altamente recomendável que o `codename` contenha somente *letras* e o caractere *underscore*. |
| 33 | + |
| 34 | + |
| 35 | + |
| 36 | +Após criada a permissão, você pode associá-la a um usuário ou um grupo de usuários. Se quiser associar a um usuário, basta acessar a página de edição do mesmo, e na seção de permissões atribuí-la ao usuário, e por fim salvar. |
| 37 | + |
| 38 | + |
| 39 | + |
| 40 | +A permissão pode ser associada também a um grupo de usuários, e para isso basta acessar a página de um grupo específico, e associar a permissão da mesma maneira que faz com qualquer outra permissão no Django. |
| 41 | + |
| 42 | +## Limitando o acesso nas views |
| 43 | + |
| 44 | +Toda view recebe um `request` como parâmetro contendo uma referência ao usuário logado. Dessa maneira é possível verificar se este usuário tem uma dada permissão usando o método `has_perm`. |
| 45 | + |
| 46 | +```python |
| 47 | +from django.core.exceptions import PermissionDenied |
| 48 | + |
| 49 | +def config_view(request): |
| 50 | + if not request.user.has_perm('global_permissions.pode_acessar_pagina_config'): |
| 51 | + raise PermissionDenied |
| 52 | + |
| 53 | + # continuar com o restante do processamento... |
| 54 | +``` |
| 55 | + |
| 56 | +No exemplo acima, se o usuário tiver a permissão `pode_acessar_pagina_config` ou pertencer a um grupo que tenha essa permissão, então passará pelo `if` sem problemas, caso contrário receberá um erro de permissão negada. |
| 57 | + |
| 58 | +Também é possível verificar se um usuário tem mais de uma permissão sem a necessidade de um `if` com vários `and`, através do método [`has_perms`](https://docs.djangoproject.com/en/dev/ref/contrib/auth/#django.contrib.auth.models.User.has_perms). |
| 59 | + |
| 60 | +## Limitando o acesso nos templates |
| 61 | + |
| 62 | +Limitar o acesso nos templates é tão simples quanto implementar nas views, mas diferentemente do primeiro, os templates já recebem automaticamente um objeto de permissões do usuário logado (desde que você utilize o *context processor* `django.contrib.auth.context_processors.auth`). Supondo que queiramos saber se o usuário pode visualizar um dado item do menu, podemos fazer da seguinte forma: |
| 63 | + |
| 64 | +```htmldjango |
| 65 | +{% if perms.global_permissions.pode_acessar_pagina_config %} |
| 66 | + <a href="/config/">Configuração</a> |
| 67 | +{% endif %} |
| 68 | +``` |
| 69 | + |
| 70 | +## Isso resolve tudo? |
| 71 | + |
| 72 | +Não. |
| 73 | + |
| 74 | +Existem casos onde pode ser útil outro tipo de arquitetura para permissões. Se você precisar limitar o acesso baseado em um registro no banco de dados, então pode usar o [django-guardian](https://github.com/lukaszb/django-guardian). Se percisar de algo mais rebuscado, o [django-permission](https://github.com/lambdalisue/django-permission) talvez seja uma escolha mais acertada. O [django-global-permissions](https://github.com/eduardo-matos/django-global-permissions) tem como foco simplificar a criação e uso de permissões globais. Se é isso que você precisa, então não precisa mais procurar por uma solução ;) |
| 75 | + |
| 76 | +## Contribuindo |
| 77 | + |
| 78 | +Caso você encontre algum bug ou tenha uma ideia de como melhorar o projeto, fique a vontade para contribuir através do [repositório no GitHub](https://github.com/eduardo-matos/django-global-permissions)! |
0 commit comments