Django-guardian: Agregar roles

Creado en 16 ene. 2011  ·  14Comentarios  ·  Fuente: django-guardian/django-guardian

En primer lugar, permítanme explicarles por qué no agregamos roles en primer lugar. En resumen, la política de Django es incluir baterías simples pero bien hechas. django.contrib.auth es el mejor ejemplo. ¿Cuántas veces la gente necesita intercambiar autenticación con su propia implementación? No creo en tantos. guardian pretende ser una especie de "extensión" de la aplicación de autenticación, con una interfaz sana y sencilla. Y eso es. No se incluyen muchas funciones adicionales (excepto algunas funciones de acceso directo, pero esto se refiere a la parte "sana").

Ahora, ¿qué pasa con los roles? ¿Todo lo que siempre necesitaron? De hecho, no lo creo. Lo mismo con los grupos, de verdad. El usuario es necesario, ya que es una entidad fundamental para la gran mayoría de todas las aplicaciones.

Ok, aquí estamos, v1.0 casi disponible. Entonces, para v1.1, el soporte de roles sería probablemente la característica más importante. Aún así, en mi opinión, debería ser "opcional" de alguna manera.

API change Enhancement

Todos 14 comentarios

+1 Pero estoy de acuerdo en que los roles son algo que casi nunca se necesita en la mayoría de las aplicaciones que están escritas en django.
De vez en cuando, alguien como nosotros intenta implementar una aplicación (sistema de gestión de documentos personalizado) para la que django no estaba realmente hecho, y cuando tienes muchas permanentes, empiezas a pensar en roles :) roles y herencia son las dos cosas que deberían convertirse en v1.1

Sin embargo, la parte opcional es fácil. al igual que usar django no te obliga a usar grupos, no hace que la gente tenga que definir roles.

gracias por la aplicación por cierto.
ceniza

+1 para roles, dependiendo de la implementación.

+1 para roles y herencia

Creo que los roles son un "grupo de permisos", no sé si esto es lo mismo para ti. Con esta clase de roles, es más fácil asignar un montón de permisos. Sugiero implementar esto usando conjuntos de Python para restar un rol a otro, u obtener la intersección, y así sucesivamente:

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

+1 para roles, herencia y opcional

Hola a todos,
Soy uno de los tipos que necesita, o le gustaría tener, permisos de objetos basados ​​en roles.

Entiendo de esta discusión que por muy buenas razones no es una prioridad para django-guardian. Entonces crearé mi propia aplicación. Sin embargo, creo que la mayoría de las características y componentes de django-guardian están bien y, si es posible, no querría reinventar la rueda sino reutilizarlos. Entonces mi pregunta es, si está dispuesto a hacer que la protección de django sea un poco más conectable. Por ejemplo (eso es solo lo primero que me vino a la mente, mientras revisaba el código): ¿establecer una configuración para ObjectPermissionChecker, que permitirá que otras aplicaciones reemplacen la clase Checker con algo que se encargue de los roles?

¿Qué piensan ustedes?

Gracias
Juergen

Hej @schacki , en realidad es una buena idea. Las pruebas probablemente fallarían para la clase de verificador no predeterminada, pero podemos marcarlas o asegurarnos de que se ejecuten solo con el verificador predeterminado. ¿Puedes crear una tarea específica para ello, por favor?

Haría que cualquier clase de verificador no predeterminada no sea parte de django-guardign sino de la "otra" aplicación, por lo que es el trabajo de la otra aplicación para las clases de verificador no predeterminado. Permítame hacer un análisis más completo de lo que podría necesitar ser adaptado y cambiado antes de abrir una tarea. ¿Con "tarea" supongo que te refieres a un tema separado? Por cierto: pueden pasar un par de semanas antes de que esto avance.

este [1] proyecto ayuda con algo?

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

Copiado el siguiente texto por @suriya de # 330:

Ha habido alguna discusión en el pasado # 23 sobre la adición de soporte de roles en django-guardian. Sin embargo, no se movió muy lejos. Probablemente esto se deba a que agregar roles a django-guardian parece una tarea muy compleja.

Creo que tengo una propuesta muy sencilla. Me gustaría presentarlo y, si hay interés, implementarlo. En la actualidad, tengo mi propia bifurcación de django-guardian que esta funcionalidad. Sería genial ver que esto también lo utilicen otros.

El núcleo de la verificación de permisos de django-guardian para una vista se implementa en la función get_403_or_None() . Esta función comprueba que un usuario tenga todos los permisos necesarios. 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)

Los roles de apoyo son un cambio menor. En lugar de comprobar que un usuario tiene permisos all , simplemente tenemos que comprobar si un usuario cumple al menos un permiso. Algo como

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)

Deberías hacerlo. Para agregar este soporte, todo lo que necesitamos es definir una nueva bandera en get_403_or_None() y sus llamadores que especifique si queremos que se satisfagan todos o solo algunos permisos.

Abrí el # 386 y me doy cuenta de que probablemente sea lo mismo. Lo que necesitaría es que en el código donde estoy verificando el permiso puedo hacer "este usuario tiene permisos de edición en este objeto", pero cuando estoy asignando permisos a los usuarios, les asigno el permiso de "moderador", que es un superconjunto de permisos múltiples, incluido el permiso de "edición". Esto me permite más adelante agregar más permisos al "moderador" sin tener que asignarlos manualmente a todos los usuarios.

Podemos nombrar esta jerarquía de permisos, de roles.

+1 para roles, herencia y opcional

Implementé un concepto de rol como el siguiente:

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

implementar personalizado 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

En django construido en vistas crud ahora es posible definir global_required_permissions y required_permissions :

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

Conclusión

Esta solución se basa en que un User tiene el permiso global para eliminar recursos, que podría ser otorgado por un Group de los modelos de autenticación de django (The Role en el contexto de este problema). Pero solo puede eliminar recursos para los que tiene el objeto basado en view_resource Permission de los modelos de autenticación de django. Creo que esta podría ser una forma común de implementar roles con django guardian.

Si obtengo algunos pulgares hacia arriba, abriré una solicitud de extracción con una versión de código libre de conflictos de mi código anterior.

Enfoque realmente interesante @jokiefer. Me gusta eso, originalmente había estado tratando de implementar guardian en ese sentido. Los usuarios necesitarían el permiso global view o edit , pero luego para acceder a un objeto específico se requería un permiso individual para view o edit . El permiso global iba a solicitar act más de un permiso de habilitación sin el cual los permisos de objeto no importaban. Obviamente, eso no funcionó, así que reestructuré las cosas.

¿Su muestra lo permitiría? es decir, se necesita el permiso global delete , pero para realmente delete un objeto también se requiere el permiso delete basado en objetos. Según su ejemplo, se necesita el delete global, y luego un objeto específico view ? ¿Es eso un poco diferente?

@dwasyl

¿Su muestra lo permitiría?
Sí, esto funcionaría para su enfoque.

Pero no me concentré en la muestra anterior. En nuestro proyecto implementamos una aplicación acl simple posible (AccessControlList). En esta aplicación hay un modelo básico llamado acl . Este modelo es una abstracción de los permisos de tutor atomic . Almacena un conjunto de usuarios, conjunto de permisos, conjunto (s) de objetos accesibles. La magia detrás de este modelo se implementa en algunas señales. Por ejemplo, ownable_models_handling.py es una señal genérica que agrega señales dinámicamente a todos los demás modelos a los que se podrá acceder con permisos de tutor. SI queremos asegurar más modelos, simplemente necesitamos implementar un nuevo campo m2m en el modelo AccessControlList , que debe llamarse con un accessible_MODELNAME inicial para que funcione automáticamente. En acl_handling.py está la implementación que agrega / elimina los permisos de guardián en función de los AccessControlList dados. Con eso no necesitamos modificar el núcleo guardián.

Quizás esta aplicación ACL te ayude más que el ejemplo anterior. Con un poco de trabajo adicional en la aplicación acl tal vez podría subcontratarse de nuestro código para una forma genérica de agregar roles a guadian (una solución de aplicación superior para guardián).

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

johnthagen picture johnthagen  ·  9Comentarios

Dzejkob picture Dzejkob  ·  28Comentarios

g-as picture g-as  ·  10Comentarios

brianmay picture brianmay  ·  16Comentarios

ad-m picture ad-m  ·  13Comentarios