Django-guardian: 役割を追加する

作成日 2011年01月16日  ·  14コメント  ·  ソース: django-guardian/django-guardian

まず、そもそも役割を追加しなかった理由を説明しましょう。 要するに、Djangoのポリシーは、シンプルでありながらよくできたバッテリーを含めることです。 django.contrib.authが最良の例です。 何回人々は自分の実装と認証を交換する必要がありますか? それほど多くはないと思います。 ガーディアンは、正気でシンプルなインターフェースを備えた、認証アプリの一種の「拡張」となることを目的としています。 以上です。 追加機能はあまり含まれていません(一部のショートカット機能を除く-ただし、これは「正常な」部分を指します)。

さて、役割はどうですか? 彼らがいつも必要としていたのは? 実際、私はそうは思わない。 本当にグループも同じです。 ユーザーは、すべてのアプリケーションの大部分の基本的なエンティティであるため、必要です。

さて、ここにあります、v1.0はほとんどそこにあります。 したがって、v1.1の場合、役割のサポートがおそらく最も重要な機能になります。 それでも、私の見解では、それは何らかの形で「オプション」であるはずです。

API change Enhancement

全てのコメント14件

+1しかし、私は、役割がdjangoで書かれた大多数のアプリではほとんど必要とされないものであることに同意します。
時々、私たちのような誰かがdjangoが実際には作られていないアプリ(カスタムドキュメント管理システム)を実装しようとします、そしてあなたがたくさんのパーマを持っているとき、あなたは役割について考え始めます:)役割と継承v1.1にする必要がある2つのことです

オプション部分は簡単です。 djangoを使用してもグループを使用できないのと同じように、人々に役割を定義させる必要はありません。

アプリのthxところで。

実装に応じて、役割の+1。

役割と継承の+1

役割は「権限のグループ」だと思いますが、これがあなたにとって同じかどうかはわかりません。 このクラスの役割を使用すると、一連の権限を簡単に割り当てることができます。 ある役割を別の役割に減算したり、共通部分を取得したりするために、Pythonセットを使用してこれを実装することをお勧めします。

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

役割、継承、およびオプションの+1

こんにちは、みんな、
私は、役割ベースのオブジェクト権限を必要としている、または持っていたいと思っている人の1人です。

この議論から、非常に正当な理由から、それはdjango-guardianの優先事項ではないことがわかります。 だから私は自分のアプリを作成します。 ただし、django-guardianのほとんどの機能とコンポーネントは問題ないと思います。可能であれば、車輪の再発明はせず、それらを再利用したいと思います。 だから私の質問は、あなたがdjango-guardingをもう少しプラグイン可能にすることをいとわないかどうかです。 たとえば(これは、コードを実行しているときに最初に頭に浮かんだことです):ObjectPermissionCheckerの設定を行います。これにより、他のアプリがCheckerクラスを役割を処理するものに置き換えることができますか?

皆さんはどう思いますか?

ありがとう
ユルゲン

Hej @schacki 、それは実際にはかなりいい考えです。 デフォルトではないチェッカークラスのテストはおそらく失敗しますが、それらにマークを付けるか、デフォルトのチェッカーのみで実行されていることを確認できます。 そのための特定のタスクを作成できますか?

デフォルト以外のチェッカークラスは、django-guardignの一部ではなく、「その他」のアプリの一部にします。したがって、デフォルト以外のチェッカークラスに対する他のアプリの仕事です。 タスクを開く前に、何を適応させて変更する必要があるかについて、より徹底的な分析をさせてください。 「タスク」とは別の問題を意味すると思いますか? ところで:これが進むまでに数週間かかるかもしれません。

this [1]プロジェクトは何か助けになりますか?

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

#330から@suriyaによって次のテキストをコピーしました:

過去#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権限を持っていることを確認する代わりに、ユーザーが少なくとも1つの権限を満たしているかどうかを確認するだけです。 何かのようなもの

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認証モデル(この問題のコンテキストでの役割)からGroupによって付与される可能性があります。 ただし、django認証モデルからオブジェクトベースのview_resource Permissionを持っているため、リソースを削除することしかできません。 これは、djangoガーディアンで役割を実装するための一般的な方法である可能性があると思います。

親指を立てたら、上記のコードの競合のないコードバージョンでプルリクエストを開きます。

本当に興味深いアプローチ@jokiefer。 私はそれが好きです、私はもともとそれらの線に沿ってguardianを実装しようとしていました。 ユーザーにはグローバルなviewまたはedit権限が必要ですが、特定のオブジェクトにアクセスするには、 viewまたはedit個別の権限が必要でした。 グローバルパーミッションは、オブジェクトパーミッションが問題にならないようにするために、より多くの有効化パーミッションを実行するように要求しようとしていました。 明らかにそれは実際には機能しなかったので、私は物事を再構築しました。

あなたのサンプルはそれを可能にしますか? つまり、グローバルdelete権限が必要ですが、実際にオブジェクトをdeleteするには、オブジェクトベースのdelete権限も必要です。 あなたの例に基づいて、グローバルdeleteが必要であり、次にオブジェクト固有のview ? それは少しミスマッチですか?

@dwasyl

あなたのサンプルはそれを可能にしますか?
はい、これはあなたのアプローチに役立ちます。

しかし、私は上記のサンプルに焦点を合わせていませんでした。 私たちのプロジェクトでは、可能な限り単純なaclアプリ(AccessControlList)を実装します。 このアプリには、 aclと呼ばれる1つのコアモデルがあります。 このモデルは、 atomic保護者の権限を抽象化したものです。 これには、ユーザーのセット、アクセス許可のセット、アクセス可能なオブジェクトのセットが格納されます。 このモデルの背後にある魔法のことは、いくつかのシグナルに実装されています。 たとえば、 ownable_models_handling.pyは、保護者の許可を得てアクセスできる他のすべてのモデルに動的にシグナルを追加する汎用シグナルです。 より多くのモデルを保護したい場合は、 AccessControlListモデルに新しいm2mフィールドを実装する必要があります。これは、自動的に機能するために先頭のaccessible_MODELNAMEで呼び出す必要があります。 acl_handling.pyには、指定されたAccessControlList基づいて保護者のアクセス許可を追加/削除する実装があります。 これで、ガーディアンコアを変更する必要はありません。

たぶん、このaclアプリは、上記のサンプルよりも役立つでしょう。 aclアプリでの追加作業により、グアディアン(保護者向けのトップアプリソリューション)に役割を追加する一般的な方法について、コードからアウトソーシングできる可能性があります。

このページは役に立ちましたか?
0 / 5 - 0 評価