Django-guardian: 添加角色

创建于 2011-01-16  ·  14评论  ·  资料来源: django-guardian/django-guardian

首先,让我解释一下为什么我们没有首先添加角色。 简而言之,Django 的政策是包括简单但做得好的电池。 django.contrib.auth 就是最好的例子。 人们需要与自己的实现交换身份验证多少次? 我相信没有那么多。 Guardian 旨在成为身份验证应用程序的“扩展”,具有健全和简单的界面。 就是这样。 没有包含太多额外的功能(除了一些快捷功能 - 但这指的是“理智”部分)。

现在,角色呢? 所有他们总是需要的? 事实上,我不相信。 与团体一样,真的。 需要用户,因为它是绝大多数应用程序的基本实体。

好的,我们到了,v1.0 快要发布了。 因此,对于 v1.1,角色支持可能是最重要的功能。 尽管如此,在我看来,它在某种程度上应该是“可选的”。

API change Enhancement

所有14条评论

+1 但我确实同意在大多数用 Django 编写的应用程序中几乎不需要角色。
像我们这样的人时不时地尝试实现一个应用程序(自定义文档管理系统),而 django 并不是真正为它设计的,当你有很多权限时,你开始考虑角色 :) 角色和继承是应该使它成为 v1.1 的两件事

可选部分虽然很简单。 就像使用 django 不会让你使用组一样,不要让人们必须定义角色。

顺便说一句,感谢应用程序。

+1 角色,取决于实现。

+1 角色和继承

我认为角色是一个“权限组”,我不知道这对你来说是否相同。 使用此类角色分配一堆权限会更容易。 我建议使用 Python 集来实现这一点,以便将一个角色减去另一个角色,或者获得交集,依此类推:

http://docs.python.org/library/stdtypes.html#set -types-set-frozenset
http://en.wikibooks.org/wiki/Python_Programming/Sets

+1 角色、继承和可选

大家好,
我是需要或希望拥有基于角色的对象权限的人之一。

我从这次讨论中了解到,出于很好的理由,它不是 django-guardian 的优先事项。 所以我将创建我自己的应用程序。 但是,我认为 django-guardian 的大多数功能和组件都很好,如果可能的话,我不想重新发明轮子而是重用它们。 所以我的问题是,如果你愿意让 django-guarding 更具可插拔性。 例如(这只是我在浏览代码时想到的第一件事):为 ObjectPermissionChecker 设置一个设置,这将允许其他应用程序用处理角色的东西替换 Checker 类?

你们有什么感想?

谢谢
于尔根

Hej @shacki ,这实际上是个不错的主意。 对于非默认检查器类,测试很可能会失败,但我们可以标记这些或确保它们仅使用默认检查器运行。 你能为它创建一个特定的任务吗?

我会让任何非默认检查器类不是 django-guardign 的一部分,而是“其他”应用程序的一部分 - 所以它是其他应用程序对非默认检查器类的工作。 在开始任务之前,请让我对可能需要调整和更改的内容进行更彻底的分析。 对于“任务”,我认为您的意思是一个单独的问题? 顺便说一句:这可能需要几周的时间才能向前推进。

这个[1]项目有什么帮助吗?

[1] https://github.com/vintasoftware/django-role-permissions

@suriya从 #330 复制以下文本:

在过去的#23 中有一些关于在 django-guardian 中添加角色支持的讨论。 然而,它并没有走多远。 这可能是因为向 django-guardian 添加角色似乎是一项非常复杂的任务。

我想我有一个非常简单的建议。 我想提出来,如果有兴趣,就实施它。 目前,我有自己的 django-guardian 分支,其中包含此功能。 很高兴看到其他人也使用它。

django-guardian 对视图的权限检查的核心是在get_403_or_None()函数中实现的。 此函数检查用户是否具有所有必需的权限。 https://github.com/lukaszb/django-guardian/blob/112c373f213a19d93baa81fa4a941a41333115b5/guardian/utils.py#L98

has_permissions = all(request.user.has_perm(perm, obj) for perm in perms)

配角是一个微小的变化。 与其检查用户是否具有all权限,我们只需检查用户是否至少满足一项权限。 就像是

if require_all_permissions:
    has_permissions = all(request.user.has_perm(perm, obj) for perm in perms)
else:
    has_permissions = any(request.user.has_perm(perm, obj) for perm in perms)

应该这样做。 要添加此支持,我们只需要在get_403_or_None()及其调用者中定义一个新标志,指定我们是希望满足所有权限还是仅满足某些权限。

我打开了#386,我意识到这可能是一样的。 我需要的是,在我检查权限的代码中,我可以执行“此用户是否对此对象具有编辑权限”,但是当我分配给用户权限时,我分配给他们“主持人”权限,这是一个多个权限的超集,包括“编辑”权限。 这允许我稍后向“主持人”添加更多权限,而无需手动将它们分配给所有用户。

我们可以命名这个权限层次结构,角色。

+1 角色、继承和可选

我实现了一个角色概念,如下所示:

自定义get_40x_or_None

from django.conf import settings
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
from django.http import HttpResponseForbidden, HttpResponseNotFound
from django.shortcuts import render
from guardian.conf import settings as guardian_settings


def get_40x_or_None(request, perms, global_perms, obj=None, login_url=None,
                    redirect_field_name=None, return_403=False,
                    return_404=False):
    """check if the given perms and global_perms are both granted by the user in combination"""
    login_url = login_url or settings.LOGIN_URL
    redirect_field_name = redirect_field_name or REDIRECT_FIELD_NAME

    # Handles both original and with object provided permission check
    # as ``obj`` defaults to None

    has_permissions = False
    if not has_permissions:
        has_permissions = all(request.user.has_perm(perm, obj) for perm in perms) and \
                          all(request.user.has_perm(perm) for perm in global_perms)

    if not has_permissions:
        if return_403:
            if guardian_settings.RENDER_403:
                response = render(request, guardian_settings.TEMPLATE_403)
                response.status_code = 403
                return response
            elif guardian_settings.RAISE_403:
                raise PermissionDenied
            return HttpResponseForbidden()
        if return_404:
            if guardian_settings.RENDER_404:
                response = render(request, guardian_settings.TEMPLATE_404)
                response.status_code = 404
                return response
            elif guardian_settings.RAISE_404:
                raise ObjectDoesNotExist
            return HttpResponseNotFound()
        else:
            from django.contrib.auth.views import redirect_to_login
            return redirect_to_login(request.get_full_path(),
                                     login_url,
                                     redirect_field_name)

实现自定义GlobalPermissionRequiredMixin

from django.core.exceptions import PermissionDenied, ImproperlyConfigured
from guardian.mixins import PermissionRequiredMixin
from collections.abc import Iterable

from permission.utils import get_40x_or_None


class GlobalPermissionRequiredMixin(PermissionRequiredMixin):
    global_permission_required = None

    def get_global_required_permissions(self, request=None):
        """
        Returns list of permissions in format *<app_label>.<codename>* that
        should be checked against *request.user* and *object*. By default, it
        returns list from ``global_permission_required`` attribute.

        :param request: Original request.
        """
        if isinstance(self.global_permission_required, str):
            perms = [self.global_permission_required]
        elif isinstance(self.global_permission_required, Iterable):
            perms = [p for p in self.global_permission_required]
        else:
            raise ImproperlyConfigured("'GlobalPermissionRequiredMixin' requires "
                                       "'global_permission_required' attribute to be set to "
                                       "'<app_label>.<permission codename>' but is set to '%s' instead"
                                       % self.global_permission_required)
        return perms

    def check_permissions(self, request):
        """
        Checks if *request.user* has all permissions returned by
        *get_required_permissions* method.

        :param request: Original request.
        """
        obj = self.get_permission_object()

        forbidden = get_40x_or_None(request,
                                    perms=self.get_required_permissions(request),
                                    global_perms=self.get_global_required_permissions(request),
                                    obj=obj,
                                    login_url=self.login_url,
                                    redirect_field_name=self.redirect_field_name,
                                    return_403=self.return_403,
                                    return_404=self.return_404,
                                    )
        if forbidden:
            self.on_permission_check_fail(request, forbidden, obj=obj)
        if forbidden and self.raise_exception:
            raise PermissionDenied()
        return forbidden

在内置 crud 视图的 Django 中,现在可以定义global_required_permissionsrequired_permissions

class ResourceDeleteView(GlobalPermissionRequiredMixin, DeleteView):
    ...
    global_permission_required = [app.delete_resource]
    permission_required = [app.view_resource]
    ...

结论

此解决方案基于User具有删除资源的全局权限,这可以由 django auth 模型中的Group授予(此问题上下文中的角色)。 但是他只能删除资源,因为他拥有来自 django auth 模型的基于对象的view_resource Permission 。 我认为这可能是使用 django Guardian 实现角色的常用方法。

如果我得到一些赞许,我将打开一个带有我上面代码的无冲突代码版本的拉取请求。

非常有趣的方法@jokiefer。 我喜欢这样,我最初一直试图沿着这些路线实现guardian 。 用户需要全局viewedit权限,但访问特定对象需要viewedit的单独权限。 全局权限将要求更多的启用权限,否则对象权限无关紧要。 显然这并没有真正起作用,所以我重组了一些东西。

你的样品允许吗? 即需要全局delete权限,但实际上delete对象还需要基于对象的delete权限。 根据您的示例,需要全局delete ,然后是特定于对象的view ? 是不是有点不搭调?

@dwasyl

你的样品允许吗?
是的,这对你的方法有用。

但我没有关注上面的示例。 在我们的项目中,我们实现了一个尽可能简单的acl应用程序(AccessControlList)。 在这个应用程序中有一个名为acl 的核心模型。 这个模型是atomic监护人权限的抽象。 它存储一组用户、一组权限、一组可访问对象。 这个模型背后的神奇之处是在一些信号中实现的。 例如ownable_models_handling.py是一个通用信号,它动态地将信号附加到所有其他模型,这些模型应具有监护人权限。 如果我们想要保护更多模型,我们只需在AccessControlList模型中实现一个新的 m2m 字段,必须使用前导accessible_MODELNAME调用它才能自动工作。 在acl_handling.py 中有一个实现,它根据给定的AccessControlList添加/删除监护人权限。 有了这个,我们不需要修改监护人核心。

也许这个 acl 应用程序对您的帮助比上面的示例更多。 在acl应用程序上进行一些额外的工作,也许它可以从我们的代码中外包,以获得一种将角色添加到 guadian(监护人的顶级应用程序解决方案)的通用方法。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

ad-m picture ad-m  ·  13评论

Dzejkob picture Dzejkob  ·  28评论

David-OConnor picture David-OConnor  ·  6评论

brianmay picture brianmay  ·  16评论

johnthagen picture johnthagen  ·  9评论