Django-guardian: Ajouter des rôles

Créé le 16 janv. 2011  ·  14Commentaires  ·  Source: django-guardian/django-guardian

Tout d'abord, laissez-moi vous expliquer pourquoi nous n'avons pas ajouté de rôles en premier lieu. Bref, la politique de Django est d'inclure des batteries simples mais bien faites. django.contrib.auth est le meilleur exemple. Combien de fois les gens ont-ils besoin d'échanger l'authentification avec leur propre implémentation ? Je crois pas tellement. Guardian est destiné à être une sorte d'"extension" de l'application d'authentification, avec une interface simple et saine. Et c'est tout. Pas beaucoup de fonctionnalités supplémentaires incluses (à l'exception de certaines fonctions de raccourci - mais cela fait référence à la partie "saine").

Maintenant, qu'en est-il des rôles ? Tout ce dont ils ont toujours eu besoin ? En fait, je ne crois pas. Même chose avec les groupes, vraiment. L'utilisateur est nécessaire car il s'agit d'une entité fondamentale pour la grande majorité de toutes les applications.

Ok, nous y sommes, v1.0 presque là-bas. Ainsi, pour la v1.1, la prise en charge des rôles serait probablement la fonctionnalité la plus importante. Pourtant, à mon avis, cela devrait être "facultatif" d'une manière ou d'une autre.

API change Enhancement

Tous les 14 commentaires

+1 Mais je suis d'accord que les rôles sont quelque chose qui n'est presque jamais nécessaire dans la majorité des applications écrites en Django.
De temps en temps, quelqu'un comme nous essaie d'implémenter une application (système de gestion de documents personnalisé) pour laquelle django n'a pas vraiment été fait, et quand vous avez beaucoup de perms, vous commencez à penser aux rôles :) rôles et héritage sont les deux choses qui devraient faire partie de la v1.1

la partie optionnelle est facile cependant. tout comme l'utilisation de django ne vous oblige pas à utiliser des groupes, cela n'oblige pas les gens à définir des rôles.

merci pour l'application d'ailleurs.
cendre

+1 pour les rôles, selon la mise en œuvre.

+1 pour les rôles et l'héritage

Je pense que les rôles sont un "groupe de permissions", je ne sais pas si c'est la même chose pour vous. Avec cette classe de rôles, il est plus facile d'attribuer un tas d'autorisations. Je suggère d'implémenter cela en utilisant des ensembles Python afin de soustraire un rôle à un autre, ou d'obtenir l'intersection, et ainsi de suite :

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

+1 pour les rôles, l'héritage et facultatif

Salut tout le monde,
Je fais partie de ceux qui ont besoin - ou aimeraient avoir - des autorisations d'objet basées sur les rôles.

Je comprends de cette discussion que pour de très bonnes raisons ce n'est pas une priorité pour django-guardian. Je vais donc créer ma propre application. Cependant, je pense que la plupart des fonctionnalités et des composants de django-guardian sont bons et si possible, je ne voudrais pas réinventer la roue mais les réutiliser. Ma question est donc de savoir si vous êtes prêt à rendre Django-guarding un peu plus enfichable. Par exemple (c'est juste la première chose qui m'est venue à l'esprit, en parcourant le code) : définissez le paramètre a pour ObjectPermissionChecker, qui permettra à d'autres applications de remplacer la classe Checker par quelque chose qui prend en charge les rôles ?

Qu'en pensez-vous?

Merci
Jürgen

Hej @schacki , c'est en fait une très bonne idée. Les tests échoueraient très probablement pour la classe de vérificateur non par défaut, mais nous pouvons les marquer ou nous assurer qu'ils sont exécutés uniquement avec le vérificateur par défaut. Pouvez-vous créer une tâche spécifique pour cela, s'il vous plaît ?

Je ferais en sorte que toutes les classes de vérificateurs autres que celles par défaut ne fassent pas partie de django-guardign mais de "l'autre" application - c'est donc le travail de l'autre application pour les classes de vérificateurs autres que celles par défaut. S'il vous plaît laissez-moi faire une analyse plus approfondie de ce qui pourrait devoir être adapté et modifié avant d'ouvrir une tâche. Avec « tâche », je suppose que vous voulez dire un problème distinct ? btw: cela peut prendre quelques semaines avant que cela avance.

ce[1] projet aide-t-il pour quoi que ce soit ?

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

Texte suivant copié par

Il y a eu des discussions dans le passé #23 sur l'ajout de la prise en charge des rôles dans django-guardian. Cependant, il n'a pas bougé loin. C'est probablement parce que l'ajout de rôles à django-guardian semble être une tâche très complexe.

Je pense avoir une proposition très simple. Je voudrais le mettre en avant et s'il y a un intérêt, le mettre en œuvre. A l'heure actuelle, j'ai mon propre fork de django-guardian qui a cette fonctionnalité. Ce serait formidable de voir cela aussi utilisé par d'autres.

Le cœur de la vérification des autorisations de django-guardian pour une vue est implémenté dans la fonction get_403_or_None() . Cette fonction vérifie qu'un utilisateur dispose de toutes les autorisations requises. 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)

Les rôles de soutien sont un changement mineur. Au lieu de vérifier qu'un utilisateur a des permissions all , nous devons simplement vérifier si un utilisateur satisfait au moins une permission. Quelque chose comme

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)

devrait le faire. Pour ajouter ce support, tout ce dont nous avons besoin est de définir un nouveau drapeau dans get_403_or_None() et ses appelants qui spécifient si nous voulons que toutes ou seulement certaines autorisations soient satisfaites.

J'ai ouvert le #386 et je me rends compte que c'est probablement la même chose. Ce dont j'aurais besoin, c'est que dans le code où je vérifie l'autorisation, je peux faire "cet utilisateur a-t-il des autorisations de modification sur cet objet", mais lorsque j'attribue des autorisations aux utilisateurs, je leur attribue une autorisation "modérateur", qui est un surensemble de plusieurs autorisations, y compris l'autorisation « modifier ». Cela me permet plus tard d'ajouter plus d'autorisations au "modérateur" sans avoir à les attribuer manuellement à tous les utilisateurs.

On peut nommer cette hiérarchie de permissions, de rôles.

+1 pour les rôles, l'héritage et facultatif

J'ai implémenté un concept de rôle comme ci-dessous :

personnaliser 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)

implémenter 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

Dans django construit dans les vues brutes il est maintenant possible de définir global_required_permissions et required_permissions :

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

Conclusion

Cette solution est basée sur le fait qu'un User a la permission globale de supprimer des ressources, ce qui pourrait être accordé par un Group des modèles d'authentification django (Le rôle dans le contexte de ce problème). Mais il ne peut supprimer des ressources que pour cela, il dispose de l'objet view_resource Permission des modèles d'authentification django. Je pense que cela pourrait être un moyen courant de mettre en œuvre des rôles avec Django Guardian.

Si j'obtiens des pouces, j'ouvrirai une demande d'extraction avec une version de code sans conflit de mon code ci-dessus.

Approche vraiment intéressante @jokiefer. J'aime ça, j'avais initialement essayé d'implémenter guardian dans ce sens. Les utilisateurs auraient besoin de l'autorisation globale view ou edit , mais alors pour accéder à un objet spécifique, une autorisation individuelle pour view ou edit était requise. L'autorisation globale allait demander d'agir davantage comme une autorisation d'activation sans laquelle les autorisations d'objet n'avaient pas d'importance. Évidemment, cela n'a pas fonctionné, alors j'ai restructuré les choses.

Votre échantillon le permettrait-il ? c'est-à-dire que l'autorisation globale delete est nécessaire, mais pour réellement delete un objet, l'autorisation delete basée sur l'objet est également requise. Sur la base de votre exemple, le delete global est nécessaire, puis un objet view spécifique à l'objet ? Est-ce un peu un décalage?

@dwasyl

Votre échantillon le permettrait-il ?
Oui, cela fonctionnerait pour votre approche.

Mais je ne me suis pas concentré sur l'échantillon ci-dessus. Dans notre projet, nous implémentons une application acl aussi simple que possible (AccessControlList). Dans cette application, il existe un modèle de base appelé acl . Ce modèle est une abstraction des permissions atomic tuteur. Il stocke un ensemble d'utilisateurs, ensemble d'autorisations, ensemble(s) d'objets accessibles. La magie derrière ce modèle est implémentée dans certains signaux. Par exemple, ownable_models_handling.py est un signal générique qui ajoute dynamiquement des signaux à tous les autres modèles qui doivent être accessibles avec des autorisations de tuteur. SI nous souhaitons sécuriser plus de modèles, nous devons simplement implémenter un nouveau champ m2m dans le modèle AccessControlList , qui doit être appelé avec un accessible_MODELNAME en tête pour fonctionner automatiquement. Dans acl_handling.py, il y a l'implémentation qui ajoute/supprime les autorisations du tuteur en fonction des AccessControlList . Avec cela, nous n'avons pas besoin de modifier le noyau gardien.

Peut-être que cette application acl vous aide plus que l'exemple ci-dessus. Avec un peu de travail supplémentaire sur l'application acl cela pourrait peut-être être externalisé à partir de notre code pour un moyen générique d'ajouter des rôles à guadian (une solution d'application de premier plan pour le tuteur).

Cette page vous a été utile?
0 / 5 - 0 notes