Django-guardian: أضف الأدوار

تم إنشاؤها على ١٦ يناير ٢٠١١  ·  14تعليقات  ·  مصدر: django-guardian/django-guardian

بادئ ذي بدء ، اسمحوا لي أن أشرح لماذا لم نضيف الأدوار في المقام الأول. باختصار ، تتمثل سياسة Django في تضمين بطاريات بسيطة ولكنها جيدة الصنع. أفضل مثال على ذلك هو django.contrib.auth. كم مرة يحتاج الناس إلى تبادل المصادقة مع التنفيذ الخاص؟ لا أصدق الكثير. القصد من guardian أن يكون نوعًا من "الامتداد" لتطبيق المصادقة ، بواجهة عاقلة وبسيطة. وهذا كل شيء. لم يتم تضمين الكثير من الوظائف الإضافية (باستثناء بعض وظائف الاختصار - ولكن هذا يشير إلى جزء "عاقل").

الآن ، ما مع الأدوار؟ كل ما يحتاجونه دائمًا؟ في الحقيقة ، لا أعتقد ذلك. نفس الشيء مع المجموعات ، حقًا. هناك حاجة للمستخدم لأنه كيان أساسي للغالبية العظمى من جميع التطبيقات.

حسنًا ، ها نحن هنا ، الإصدار 1.0 تقريبًا موجود. لذلك بالنسبة للإصدار 1.1 ، من المحتمل أن يكون دعم الأدوار أهم ميزة. لا يزال ، في رأيي ، يجب أن يكون "اختياري" بطريقة ما.

API change Enhancement

ال 14 كومينتر

+1 لكني أوافق على أن الأدوار هي شيء لا تحتاجه معظم التطبيقات المكتوبة بلغة django.
بين الحين والآخر ، يحاول شخص ما مثلنا تنفيذ تطبيق (نظام مخصص لإدارة المستندات) لم يتم تصميم django من أجله حقًا ، وعندما يكون لديك الكثير من الامتيازات ، تبدأ في التفكير في الأدوار :) الأدوار والميراث هما الشيئان اللذان يجب تحويلهما إلى الإصدار 1.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 أكثر قابلية للتوصيل قليلاً. على سبيل المثال (هذا هو أول شيء يظهر في ذهني أثناء تصفح الكود): قم بإعداد إعداد ObjectPermissionChecker ، والذي سيسمح للتطبيقات الأخرى باستبدال فئة Checker بشيء يهتم بالأدوار؟

ماذا تظنون يا جماعة؟

شكرا
يورجن

Hejschacki ، هذه في الواقع فكرة جميلة جدًا. من المحتمل أن تفشل الاختبارات لفئة المدقق غير الافتراضية ولكن يمكننا تحديدها أو التأكد من تشغيلها باستخدام المدقق الافتراضي فقط. هل يمكنك إنشاء مهمة محددة لها من فضلك؟

أود أن أجعل أي فئات مدقق غير افتراضية ليست جزءًا من 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

في django المدمج في عروض Crud ، أصبح من الممكن الآن تحديد global_required_permissions و required_permissions :

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

استنتاج

يعتمد هذا الحل على أن User لديه الإذن العام لحذف الموارد ، والذي يمكن منحه بواسطة Group من نماذج المصادقة django (الدور في سياق هذه المشكلة). ولكن يمكنه فقط حذف الموارد لأنه يمتلك الكائن الذي يعتمد على view_resource Permission من نماذج المصادقة django. أعتقد أن هذا يمكن أن يكون طريقة شائعة لتنفيذ الأدوار مع django guardian.

إذا حصلت على بعض الإعجاب ، فسأفتح طلب سحب بإصدار رمز خالٍ من التعارض من الكود الخاص بي أعلاه.

نهج مثير للاهتمام حقًا @ jokiefer. يعجبني ذلك ، لقد كنت في الأصل أحاول تنفيذ guardian على هذا المنوال. سيحتاج المستخدمون إلى الإذن العالمي view أو edit ، ولكن بعد ذلك للوصول إلى كائن معين ، كان مطلوبًا الحصول على إذن فردي لـ view أو edit . كان الإذن العالمي سيطلب إجراء المزيد من الإذن التمكيني الذي بدونه لا تهم أذونات الكائن. من الواضح أن هذا لم ينجح في الواقع ، لذا قمت بإعادة هيكلة الأشياء.

هل تسمح عينتك بذلك؟ على سبيل المثال ، يلزم الحصول على إذن delete ، ولكن في الواقع ، يلزم الحصول على إذن delete على الكائن للحصول على كائن delete . بناءً على المثال الخاص بك ، يلزم وجود delete ، ثم عنصر محدد view ؟ هل هذا نوع من عدم التطابق؟

تضمين التغريدة

هل تسمح عينتك بذلك؟
نعم هذا من شأنه أن يعمل من أجل نهجك.

لكنني لم أركز على العينة أعلاه. في مشروعنا نقوم بتنفيذ تطبيق بسيط قدر الإمكان acl (AccessControlList). يوجد في هذا التطبيق نموذج أساسي واحد يسمى acl . هذا النموذج هو تجريد لأذونات الوصي atomic . يخزن مجموعة من المستخدمين ، مجموعة من الأذونات ، مجموعة (مجموعات) من الكائنات التي يمكن الوصول إليها. يتم تنفيذ الشيء السحري وراء هذا النموذج في بعض الإشارات. على سبيل المثال ، Owable_models_handling.py هي إشارة عامة AccessControlList ، والذي يجب استدعاؤه باستخدام accessible_MODELNAME رائد للعمل تلقائيًا. في acl_handling.py هناك تطبيق يضيف / يزيل أذونات الوصي بناءً على AccessControlList . مع ذلك لا نحتاج إلى تعديل جوهر الوصي.

ربما يساعدك تطبيق acl هذا أكثر من النموذج أعلاه. مع بعض العمل الإضافي على تطبيق acl ربما يمكن الاستعانة بمصادر خارجية من الكود الخاص بنا للحصول على طريقة عامة لإضافة أدوار إلى Guadian (حل تطبيق علوي للوصي).

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات

القضايا ذات الصلة

johnthagen picture johnthagen  ·  9تعليقات

Allan-Nava picture Allan-Nava  ·  4تعليقات

xuhcc picture xuhcc  ·  10تعليقات

BenDevelopment picture BenDevelopment  ·  5تعليقات

David-OConnor picture David-OConnor  ·  6تعليقات