web-dev-qa-db-fra.com

Comment puis-je utiliser les autorisations Django sans définir de type ou de modèle de contenu?

Je voudrais utiliser un système basé sur des autorisations pour restreindre certaines actions dans mon Django. Ces actions n'ont pas besoin d'être liées à un modèle particulier (par exemple, l'accès aux sections de l'application, la recherche .. .), donc je ne peux pas utiliser le stock permissions framework directement, car le modèle Permission nécessite une référence à un type de contenu installé.

Je pourrais écrire mon propre modèle d'autorisation, mais je devrais alors réécrire tous les goodies inclus avec les autorisations Django, telles que:

J'ai vérifié certaines applications comme Django-Authority et Django-guardian , mais elles semblent fournir des autorisations encore plus couplées au système de modèles, en autorisant des autorisations par objet.

Existe-t-il un moyen de réutiliser ce framework sans avoir défini de modèle (à part User et Group) pour le projet?

72
Chewie

Le modèle Permission de Django nécessite une instance ContentType .

Je pense que dans un sens, créer un ContentType factice qui n'est lié à aucun modèle (le app_label et model peuvent être définis sur n'importe quelle valeur de chaîne).

Si vous voulez que tout soit propre et agréable, vous pouvez créer un Permissionmodèle proxy qui gère tous les moches détails du mannequin ContentType et crée des instances d'autorisation "sans modèle" . Vous pouvez également ajouter un gestionnaire personnalisé qui filtre toutes les instances Permission liées aux modèles réels.

47
Gonzalo

Pour ceux d'entre vous qui recherchent toujours:

Vous pouvez créer un modèle auxiliaire sans table de base de données. Ce modèle peut apporter à votre projet toute autorisation dont vous avez besoin. Il n'est pas nécessaire de traiter ContentType ou de créer des objets d'autorisation explicites.

from Django.db import models

class RightsSupport(models.Model):

    class Meta:

        managed = False  # No database table creation or deletion  \
                         # operations will be performed for this model. 

        permissions = ( 
            ('customer_rights', 'Global customer rights'),  
            ('vendor_rights', 'Global vendor rights'), 
            ('any_rights', 'Global any rights'), 
        )

Juste après manage.py migrate vous pouvez utiliser ces autorisations comme les autres.

# Decorator

@permission_required('app.customer_rights')
def my_search_view(request):
    …

# Inside a view

def my_search_view(request):
    request.user.has_perm('app.customer_rights')

# In a template
# The currently logged-in user’s permissions are stored in the template variable {{ perms }}

{% if perms.app.customer_rigths %}  
    <p>You can do any customer stuff</p>
{% endif %}
97
Dmitry

Suivant les conseils de Gonzalo , j'ai utilisé un modèle proxy et un gestionnaire personnalisé pour gérer mes autorisations "sans modèle" avec un type de contenu factice.

from Django.db import models
from Django.contrib.auth.models import Permission
from Django.contrib.contenttypes.models import ContentType


class GlobalPermissionManager(models.Manager):
    def get_query_set(self):
        return super(GlobalPermissionManager, self).\
            get_query_set().filter(content_type__name='global_permission')


class GlobalPermission(Permission):
    """A global permission, not attached to a model"""

    objects = GlobalPermissionManager()

    class Meta:
        proxy = True

    def save(self, *args, **kwargs):
        ct, created = ContentType.objects.get_or_create(
            name="global_permission", app_label=self._meta.app_label
        )
        self.content_type = ct
        super(GlobalPermission, self).save(*args, **kwargs)
48
Chewie

Correction de la réponse de Chewie dans Django 1.8, comme demandé dans quelques commentaires.

Il est dit dans les notes de version:

Le champ de nom de Django.contrib.contenttypes.models.ContentType a été supprimé par une migration et remplacé par une propriété. Cela signifie qu'il n'est plus possible d'interroger ou de filtrer un ContentType par ce champ.

C'est donc le "nom" en référence dans ContentType que l'utilisent pas dans GlobalPermissions.

Lorsque je le répare, j'obtiens ce qui suit:

from Django.db import models
from Django.contrib.auth.models import Permission
from Django.contrib.contenttypes.models import ContentType


class GlobalPermissionManager(models.Manager):
    def get_queryset(self):
        return super(GlobalPermissionManager, self).\
            get_queryset().filter(content_type__model='global_permission')


class GlobalPermission(Permission):
    """A global permission, not attached to a model"""

    objects = GlobalPermissionManager()

    class Meta:
        proxy = True
        verbose_name = "global_permission"

    def save(self, *args, **kwargs):
        ct, created = ContentType.objects.get_or_create(
            model=self._meta.verbose_name, app_label=self._meta.app_label,
        )
        self.content_type = ct
        super(GlobalPermission, self).save(*args)

La classe GlobalPermissionManager est inchangée mais incluse pour être complète.

8
rgammans

Ceci est une solution alternative. Demandez-vous d'abord: pourquoi ne pas créer un modèle fictif qui existe vraiment dans la base de données mais qui n'est jamais utilisé, sauf pour les autorisations de détention? Ce n'est pas agréable, mais je pense que c'est une solution valable et simple.

from Django.db import models

class Permissions(models.Model):

    can_search_blue_flower = 'my_app.can_search_blue_flower'

    class Meta:
        permissions = [
            ('can_search_blue_flower', 'Allowed to search for the blue flower'),
        ]

La solution ci-dessus présente l'avantage que vous pouvez utiliser la variable Permissions.can_search_blue_flower dans votre code source au lieu d'utiliser la chaîne littérale "my_app.can_search_blue_flower". Cela signifie moins de fautes de frappe et plus de saisie semi-automatique dans IDE.

1
guettli

Au lieu d'écrire et d'exécuter ce code qui insère des enregistrements dans la base de données, vous pouvez simplement insérer les enregistrements dans votre base de données (en modifiant évidemment les clés primaires et étrangères selon les besoins)

insert into Django_content_type(id,name,app_label,model) values (22,'app_permission','myapp','app_permission');
insert into auth_permission(id,name,content_type_id,codename) values (64,'Is Staff Member',22,'staff_member');

Et puis, dans votre administrateur d'application, vous auriez la possibilité d'affecter "Est membre du personnel" à vos utilisateurs ou groupes. Pour vérifier cette autorisation dans votre classe, vous écririez

from Django.contrib.auth.decorators import permission_required
from Django.utils.decorators import method_decorator
from Django.views.generic import TemplateView

class MyClass(TemplateView):
    template_name = myapp/index.html'

    @method_decorator(permission_required(['myapp.staff_member'],raise_exception=True))
    def dispatch(self, *args, **kwargs):
        return super(MyClass, self).dispatch(*args, **kwargs)
1
Lehrian