Eduardo Matos
Publicado em:

Sat 22 November 2014

←Home

Solução (quase) definitiva para permissões em projetos Django

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.

O que está faltando?

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 presente na tupla INSTALLED_APPS.

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.

Qual a solução?

Devido a ser um problema recorrente, publiquei um pacote no PyPI chamado Django Global Permissions, que possibilita a criação de permissões genéricas, resolvendo o empecilho que mencionei anteriormente.

Para utilizar esse pacote, basta instalá-lo e adicioná-lo à tupla INSTALLED_APPS.

pip install django-global-permissions
INSTALLED_APPS += ('global_permissions',)

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.

Criando permissão

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.

Permissões de usuário

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.

Limitando o acesso nas views

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.

from django.core.exceptions import PermissionDenied

def config_view(request):
    if not request.user.has_perm('global_permissions.pode_acessar_pagina_config'):
        raise PermissionDenied

    # continuar com o restante do processamento...

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.

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.

Caso você prefira class based views (assim como eu), as alterações são pequenas em relação ao exemplo acima. Por padrão todas as class based views tem uma propriedade chamada request, que podemos usar acessar o usuário logado e fazer as verificações de permissão.

from django.view.generic import TemplateView
from django.core.exceptions import PermissionDenied

class ConfigView(TemplateView):
    template_name = 'config.html'

    def render_to_responde(self):
        if not self.request.user.has_perm('global_permissions.pode_acessar_pagina_config'):
            raise PermissionDenied

        # continuar com o restante do processamento...

Ainda é possível deixar o código mais simples através do pacote django-braces. Com ele temos acesso ao PermissionRequiredMixin, que automaticamente faz a verificação de permissão para nós.

from django.view.generic import TemplateView
from brace.views import PermissionRequiredMixin

class ConfigView(PermissionRequiredMixin, TemplateView):
    template_name = 'config.html'
    permission_required = 'global_permissions.pode_acessar_pagina_config'

Para mais informações sobre o django-braces, acesse sua documentação oficial.

Limitando o acesso nos templates

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:

{% if perms.global_permissions.pode_acessar_pagina_config %}
    <a href="/config/">Configuração</a>
{% endif %}

Isso resolve tudo?

Não.

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. Se percisar de algo mais rebuscado, o django-permission talvez seja uma escolha mais acertada. O 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 ;)

Contribuindo

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!

Topo
comments powered by Disqus