Django-guardian

背景

在上一篇文章Django 内置权限管理中,我们了解了Django自带的权限系统。但由于其更多的是对全局权限的管理,对于更细粒度的权限无法管控。所以,在本篇文章中,我们将对其扩展,介绍一种更细粒度的权限管理----对象权限管理

概述

Django-guardian基于django的原生逻辑扩展了django的权限机制,应用django-guardian后,可以使用django-guardian提供的方法以及django的原生方法检查全局权限,django-guardian提供的object permission机制使django的权限机制更加完善。

特性

  • Django对象权限管理
  • 匿名用户支持
  • 高级别API
  • 健壮的测试
  • Django admin集成
  • 装饰器

安装

此应用要求Django1.7或更高版本。 安装Django-guardian,使用: pip install django-guardianeasy_install django-guardian

配置

  1. 添加 guardian 到 INSTALLED_APPS:

     INSTALLED_APPS = (
         # ...
         'guardian',
     )
    
  2. 挂载guardian认证后端:

     AUTHENTICATION_BACKENDS = (
         'django.contrib.auth.backends.ModelBackend', # this is default
         'guardian.backends.ObjectPermissionBackend',
     )
    

Prepare permissions

class Task(models.Model):
    summary = models.CharField(max_length=32)
    content = models.TextField()
    reported_by = models.ForeignKey(User, on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        permissions = (
            ('view_task', 'View task'),
        )

在我们调用管理命令 makemigrationsmigrate后,view_task 权限将添加到默认权限集。

Assign object permissions

我们可以使用guardian.shortcuts.assign_perm()方法可以为用户/组分配对象权限

For user

首先准备创建User以及Task:

>>> from django.contrib.auth.models import User
>>> boss = User.objects.create(username='Big Boss')
>>> joe = User.objects.create(username='joe')
>>> task = Task.objects.create(summary='Some job', content='', reported_by=boss)
>>> joe.has_perm('view_task', task)
False

然后让我们对Joe创建一个对象权限:

>>> from guardian.shortcuts import assign_perm
>>> assign_perm('view_task', joe, task)
>>> joe.has_perm('view_task', task)
True

For group

为group分配对象权限与为user分配权限大体相同,只不过User实例变成了Group实例。

>>> from django.contrib.auth.models import Group
>>> group = Group.objects.create(name='employees')
>>> assign_perm('change_task', group, task)
>>> joe.has_perm('change_task', task)
False
>>> # Well, joe is not yet within an *employees* group
>>> joe.groups.add(group)
>>> joe.has_perm('change_task', task)
True

Another example:

>>> from django.contrib.auth.models import User, Group
>>> from guardian.shortcuts import assign_perm
# fictional companies
>>> companyA = Company.objects.create(name="Company A")
>>> companyB = Company.objects.create(name="Company B")
# create groups
>>> companyUserGroupA = Group.objects.create(name="Company User Group A")
>>> companyUserGroupB = Group.objects.create(name="Company User Group B")
# assign object specific permissions to groups
>>> assign_perm('change_company', companyUserGroupA, companyA)
>>> assign_perm('change_company', companyUserGroupB, companyB)
# create user and add it to one group for testing
>>> userA = User.objects.create(username="User A")
>>> userA.groups.add(companyUserGroupA)
>>> userA.has_perm('change_company', companyA)
True
>>> userA.has_perm('change_company', companyB)
False
>>> userB = User.objects.create(username="User B")
>>> userB.groups.add(companyUserGroupB)
>>> userB.has_perm('change_company', companyA)
False
>>> userB.has_perm('change_company', companyB)
True

Check object permissions

当我们分配了对象权限后,我们需要详细了解如何检查对象权限

has_perm

has_perm是权限检查的标准方法。通常检查是否允许 Joe 更改 Site 对象, 我们在用户实例上调用 has_perm 方法:

>>> joe.has_perm('sites.change_site')
False

对于特定的站点实例, 我们执行相同的操作, 但我们传递 site 作为附加参数:

>>> site = Site.objects.get_current()
>>> joe.has_perm('sites.change_site', site)
False

让我们分配权限后再检查:

>>> from guardian.shortcuts import assign_perm
>>> assign_perm('sites.change_site', joe, site)
<UserObjectPermission: example.com | joe | change_site>
>>> joe = User.objects.get(username='joe')
>>> joe.has_perm('sites.change_site', site)
True

get_perms

get_perms()方法通用可用于检查用户的权限,与user.has_perm()异曲同工,如:

from guardian.shortcuts import get_perms

joe = User.objects.get(username='joe')
site = Site.objects.get_current()

#############################
# It works! 
#############################
 if not 'change_site' in get_perms(joe, site):
   raise HttpResponse('Forbidden')

#############################
# It works, too!
#############################
if joe.has_perm('sites.change_post', site)
  return HttpResponse('Forbidden')

大多数情况下用原生的user.has_perm方法即可,但user和group均可作为get_perms()的传入参数,某些情况下可以使代码更简洁。

get_objects_for_user

有时需要根据特定用户、对象类型和提供的权限来提取对象列表。例如, 假设在具有自定义 view_project 权限的项目应用程序中存在项目模型。我们希望向用户展示他们实际可以查看的项目。使用 get_objects_for_user 可以很容易地实现这一点:

from django.shortcuts import render_to_response
from django.template import RequestContext
from projects.models import Project
from guardian.shortcuts import get_objects_for_user

def user_dashboard(request, template_name='projects/dashboard.html'):
    projects = get_objects_for_user(request.user, 'projects.view_project')
    return render_to_response(template_name, {'projects': projects},
        RequestContext(request))

此函数的相关API操作可详见官方文档

ObjectPermissionChecker

django-guardian 的核心模块中, 有一个 guardian.core.ObjectPermissionChecker,用于 检查用户/组对特定对象的权限。它会缓存结果, 以便在我们多次检查权限的代码部分使用它。

>>> joe = User.objects.get(username='joe')
>>> site = Site.objects.get_current()
>>> from guardian.core import ObjectPermissionChecker
>>> checker = ObjectPermissionChecker(joe) # we can pass user or group
>>> checker.has_perm('change_site', site)
True
>>> checker.has_perm('add_site', site) # no additional query made
False
>>> checker.get_perms(site)
[u'change_site']

permission_required

guardian.decorators.permission_required是django-guardian权限检查的decorator,既可以检查全局权限,又可以检查对象权限(object permission),其中,accept_global_perms参数指出是否检查user的global permission,如:

@permission_required('auth.change_user', return_403=True)
def my_view(request):
    return HttpResponse('Hello')

@permission_required('auth.change_user', (User, 'username', 'username'))
def my_view(request, username):
    '''
    auth.change_user permission would be checked based on given
    'username'. If view's parameter would be named ``name``, we would
    rather use following decorator::

        @permission_required('auth.change_user', (User, 'username', 'name'))
    '''
    user = get_object_or_404(User, username=username)
    return user.get_absolute_url()

@permission_required('auth.change_user',
    (User, 'username', 'username', 'groups__name', 'group_name'))
def my_view(request, username, group_name):
    '''
    Similar to the above example, here however we also make sure that
    one of user's group is named same as request's ``group_name`` param.
    '''
    user = get_object_or_404(User, username=username,
        group__name=group_name)
    return user.get_absolute_url()

decorator中的(User, 'username', 'username', 'groups__name', 'group_name')部分,用于指定object实例,如果忽略这个参数,则不论accept_global_perms值为True还是False,均仅仅检查全局权限。前一个username代表用户检索object实例所用的字段,后一个username与所装饰的函数的参数username对应。若所装饰的函数里参数username改成name,则装饰器的后一个username也应该改成name.group道理相同。

Remove object permissions

Example:

Let’s get back to our fellow Joe:

>>> site = Site.object.get_current()
>>> joe.has_perm('change_site', site)
True

Now, simply remove this permission:

>>> from guardian.shortcuts import remove_perm
>>> remove_perm('change_site', joe, site)
>>> joe = User.objects.get(username='joe')
>>> joe.has_perm('change_site', site)
False

Admin integration

django-guardian与Django自带的Admin管理应用集成非常简单,只需要使用GuardedModelAdmin代替原先的django.contrib.admin.ModelAdmin,然后将model注册到Admin中:

from django.contrib import admin

from posts.models import Post

from guardian.admin import GuardedModelAdmin


class PostAdmin(GuardedModelAdmin):
    prepopulated_fields = {"slug": ("title",)}
    list_display = ('title', 'slug', 'created_at')
    search_fields = ('title', 'content')
    ordering = ('-created_at',)
    date_hierarchy = 'created_at'

admin.site.register(Post, PostAdmin)
Copyright © itrunner.cn 2020 all right reserved,powered by Gitbook该文章修订时间: 2022-08-28 07:44:16

results matching ""

    No results matching ""