Django-guardian: Rollen hinzufügen

Erstellt am 16. Jan. 2011  ·  14Kommentare  ·  Quelle: django-guardian/django-guardian

Lassen Sie mich zunächst erklären, warum wir überhaupt keine Rollen hinzugefügt haben. Kurz gesagt, Djangos Politik besteht darin, einfache, aber gut gemachte Batterien zu verwenden. django.contrib.auth ist das beste Beispiel. Wie oft müssen die Benutzer die Authentifizierung mit der eigenen Implementierung austauschen? Ich glaube nicht so viele. guardian soll eine Art "Erweiterung" der Authentifizierungs-App sein, mit einer vernünftigen und einfachen Benutzeroberfläche. Und das ist es. Nicht viele zusätzliche Funktionen enthalten (außer einigen Shortcut-Funktionen - aber dies bezieht sich auf den "gesunden" Teil).

Was ist nun mit Rollen? Alles, was sie immer brauchten? Tatsächlich glaube ich nicht. Genauso bei Gruppen. Der Benutzer wird benötigt, da dies eine grundlegende Einheit für die überwiegende Mehrheit aller Anwendungen ist.

Ok, hier sind wir, v1.0 ist fast da draußen. Für v1.1 wäre die Rollenunterstützung wahrscheinlich das wichtigste Feature. Dennoch sollte es meiner Meinung nach in gewisser Weise "optional" sein.

API change Enhancement

Alle 14 Kommentare

+1 Aber ich stimme zu, dass Rollen in den meisten in Django geschriebenen Apps fast nie benötigt werden.
Hin und wieder versucht jemand wie wir, eine App (benutzerdefiniertes Dokumentenverwaltungssystem) zu implementieren, für die django nicht wirklich gemacht ist, und wenn Sie viele Perms haben, denken Sie über Rollen nach :) Rollen und Vererbung sind die zwei Dinge, die es in v1.1 schaffen sollten

Der optionale Teil ist jedoch einfach. Genauso wie die Verwendung von Django Sie nicht dazu zwingt, Gruppen zu verwenden, müssen die Leute keine Rollen definieren.

Danke für die App übrigens.
Asche

+1 für Rollen, je nach Implementierung.

+1 für Rollen und Vererbung

Ich denke, Rollen sind eine "Gruppe von Berechtigungen", ich weiß nicht, ob dies für Sie dasselbe ist. Mit dieser Rollenklasse ist es einfacher, eine Reihe von Berechtigungen zuzuweisen. Ich schlage vor, dies mit Python-Sets zu implementieren, um eine Rolle von einer anderen abzuziehen oder die Schnittmenge zu erhalten, und so weiter:

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

+1 für Rollen, Vererbung und optional

Hallo zusammen,
Ich bin einer von denen, die rollenbasierte Objektberechtigungen benötigen oder haben möchten.

Ich verstehe aus dieser Diskussion, dass es aus sehr guten Gründen keine Priorität für django-guardian hat. Also werde ich meine eigene App erstellen. Ich denke jedoch, dass die meisten Funktionen und Komponenten von django-guardian in Ordnung sind und wenn möglich würde ich das Rad nicht neu erfinden, sondern wiederverwenden. Meine Frage ist also, ob Sie bereit sind, Django-Guarding ein wenig steckbarer zu machen. Zum Beispiel (das ist nur das erste, was mir beim Durcharbeiten des Codes in den Sinn kam): eine Einstellung für ObjectPermissionChecker vornehmen, die es anderen Apps ermöglicht, die Checker-Klasse durch etwas zu ersetzen, das sich um Rollen kümmert?

Was denkt ihr?

Vielen Dank
Jürgen

Hej @schacki , das ist eigentlich eine ganz nette Idee. Tests würden höchstwahrscheinlich für Nicht-Standard-Checker-Klassen fehlschlagen, aber wir können diese markieren oder sicherstellen, dass sie nur mit dem Standard-Checker ausgeführt werden. Kannst du bitte eine spezielle Aufgabe dafür erstellen?

Ich würde alle nicht standardmäßigen Checker-Klassen nicht zu einem Teil von django-guardign, sondern der "anderen" App machen - es ist also die Aufgabe der anderen App, die nicht standardmäßigen Checker-Klassen zu verwenden. Bitte lassen Sie mich vor dem Öffnen einer Aufgabe eine gründlichere Analyse durchführen, was möglicherweise angepasst und geändert werden muss. Ich nehme an, mit "Aufgabe" meinst du ein separates Thema? btw: Es kann ein paar Wochen dauern, bis dies voranschreitet.

Dieses[1] Projekt hilft bei irgendetwas?

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

Folgender Text von @suriya aus #330

In der Vergangenheit #23 gab es einige Diskussionen über das Hinzufügen von Rollenunterstützung in django-guardian. Es ging jedoch nicht weit. Dies liegt wahrscheinlich daran, dass das Hinzufügen von Rollen zu django-guardian eine sehr komplexe Aufgabe zu sein scheint.

Ich glaube, ich habe einen ganz einfachen Vorschlag. Ich möchte es vorschlagen und bei Interesse umsetzen. Zur Zeit habe ich meinen eigenen Django-Guardian-Fork, der diese Funktionalität hat. Es wäre schön, wenn dies auch von anderen genutzt würde.

Der Kern der Berechtigungsprüfung von django-guardian für eine Ansicht ist in der Funktion get_403_or_None() implementiert. Diese Funktion prüft, ob ein Benutzer über alle erforderlichen Berechtigungen verfügt. 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)

Nebenrollen ist eine kleine Änderung. Anstatt zu überprüfen, ob ein Benutzer über all Berechtigungen verfügt, müssen wir lediglich prüfen, ob ein Benutzer mindestens eine Berechtigung erfüllt. Etwas wie

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)

Sollte es tun. Um diese Unterstützung hinzuzufügen, müssen wir lediglich ein neues Flag in get_403_or_None() und seinen Aufrufern definieren, das angibt, ob alle oder nur einige Berechtigungen erfüllt werden sollen.

Ich habe # 386 geöffnet und mir wird klar, dass dies wahrscheinlich dasselbe ist. Was ich brauche, ist, dass ich in dem Code, in dem ich die Berechtigung überprüfe, tun kann "Hat dieser Benutzer Bearbeitungsberechtigungen für dieses Objekt", aber wenn ich Benutzern Berechtigungen zuweist, weise ich ihnen die Berechtigung "Moderator" zu, was a . ist Obermenge mehrerer Berechtigungen, einschließlich der Berechtigung "Bearbeiten". Dadurch kann ich später dem "Moderator" weitere Berechtigungen hinzufügen, ohne diese manuell allen Benutzern zuweisen zu müssen.

Wir können diese Hierarchie von Berechtigungen, von Rollen benennen.

+1 für Rollen, Vererbung und optional

Ich habe ein Rollenkonzept wie folgt implementiert:

get_40x_or_None anpassen

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)

benutzerdefinierte GlobalPermissionRequiredMixin implementieren

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

In django eingebauten Crud-Ansichten ist es jetzt möglich, global_required_permissions und required_permissions :

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

Abschluss

Diese Lösung basiert darauf, dass ein User die globale Berechtigung zum Löschen von Ressourcen hat, die von einem Group aus den Django-Auth-Modellen erteilt werden könnte (Die Rolle im Kontext dieses Problems). Aber er kann nur Ressourcen löschen, für die er die objektbasierten view_resource Permission aus den Django-Authentifizierungsmodellen hat. Ich denke, dies könnte eine gängige Möglichkeit sein, Rollen mit Django Guardian zu implementieren.

Wenn ich ein paar Daumen hoch bekomme, werde ich eine Pull-Anfrage mit einer konfliktfreien Codeversion meines obigen Codes öffnen.

Wirklich interessanter Ansatz @jokiefer. Das gefällt mir, ich hatte ursprünglich versucht, guardian in diese Richtung zu implementieren. Benutzer benötigen die globale Berechtigung view oder edit , aber um auf ein bestimmtes Objekt zuzugreifen, ist eine individuelle Berechtigung für view oder edit erforderlich. Die globale Berechtigung würde eher eine aktivierende Berechtigung verlangen, ohne die die Objektberechtigungen keine Rolle spielten. Offensichtlich hat das nicht wirklich funktioniert, also habe ich die Dinge umstrukturiert.

Würde Ihre Probe das zulassen? dh die globale delete Berechtigung wird benötigt, aber um tatsächlich ein delete Objekt zu erstellen, ist auch die objektbasierte delete Berechtigung erforderlich. Basierend auf Ihrem Beispiel wird das globale delete benötigt und dann ein objektspezifisches view ? Ist das ein bisschen ein Missverhältnis?

@dwasyl

Würde Ihre Probe das zulassen?
Ja, das würde für Ihren Ansatz funktionieren.

Aber ich habe mich nicht auf das Beispiel oben konzentriert. In unserem Projekt implementieren wir eine möglichst einfache acl App (AccessControlList). In dieser App gibt es ein Kernmodell namens acl . Dieses Modell ist eine Abstraktion der atomic Wächterberechtigungen. Es speichert eine Reihe von Benutzern, eine Reihe von Berechtigungen und eine Reihe von zugänglichen Objekten. Die Magie hinter diesem Modell ist in einigen Signalen implementiert. Zum Beispiel ist ownable_models_handling.py ein generisches Signal, das dynamisch Signale an alle anderen Modelle anhängt, die mit Wächterrechten zugänglich sein sollen. Wenn wir mehr Modelle sichern möchten, müssen wir einfach ein neues m2m-Feld im AccessControlList Modell implementieren, das mit einem führenden accessible_MODELNAME aufgerufen werden muss, damit es automatisch funktioniert. In der acl_handling.py gibt es die Implementierung, die die Wächterberechtigungen basierend auf den angegebenen AccessControlList hinzufügt/entfernt. Damit müssen wir den Wächterkern nicht modifizieren.

Vielleicht hilft Ihnen diese acl-App mehr als das obige Beispiel. Mit etwas zusätzlicher Arbeit an der acl App könnte sie vielleicht aus unserem Code ausgelagert werden, um eine generische Möglichkeit zum Hinzufügen von Rollen zu guadian (einer Top-App-Lösung für Wächter) zu finden.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

BenDevelopment picture BenDevelopment  ·  5Kommentare

g-as picture g-as  ·  10Kommentare

David-OConnor picture David-OConnor  ·  6Kommentare

brianmay picture brianmay  ·  16Kommentare

Allan-Nava picture Allan-Nava  ·  35Kommentare