• 12 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_video

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 22/03/2024

Attribuez des permissions en utilisant les groupes

Restreignez des aspects de la fonctionnalité du site avec les permissions

Lorsque vous développez un site, vous serez souvent dans le cas où différents utilisateurs ont besoin d’interagir de façon différente avec certaines parties du site. Vous devrez peut-être restreindre certaines fonctionnalités à des utilisateurs spécifiques. Pour cela, utilisez des permissions.

Pour notre site web, nous allons restreindre l’accès à la création, la modification, et la suppression des modèles  Photo  et   Blog  aux utilisateurs de type   CREATOR.

Chaque modèle créé en Django a quatre permissions qui sont générées en parallèle. Pour le modèle  Photo  , ce sont les suivantes :

  • blog.add_photo  — ou, plus généralement   <app>.add_<model>

  • blog.change_photo  — ou   <app>.change_<model>

  • blog.delete_photo  — ou   <app>.delete_<model>

  • blog.view_photo  — ou   <app>.view_<model>

Django utilise ces permissions en interne pour gérer l’accès au site administrateur, mais vous pouvez également les utiliser dans votre code. Découvrez en vidéo comment restreindre les fonctionnalités du site selon les permissions de l'utilisateur.

Étape 1 : Restreignez l’accès dans la vue

Tout d’abord, voyons comment restreindre l’accès à la vue en utilisant les permissions.

Tout comme vous utilisez le décorateur  @login_required  pour restreindre l’accès aux utilisateurs en fonction de s’ils sont ou non connectés, vous pouvez utiliser le décorateur  @permission_required  pour limiter l’accès en fonction de la permission. Dans ce cas, la seule différence est que vous spécifiez la permission requise comme argument au décorateur.

Restreignons l’accès à la vue  photo_upload  aux seuls utilisateurs qui ont la permission  blog.add_photo  :

from django.contrib.auth.decorators import login_required, permission_required



@login_required
@permission_required('blog.add_photo', raise_exception=True)
def photo_upload(request):
    ...

Maintenant, essayez d’accéder à http://localhost:8000/photo/upload en étant connecté. Vous verrez que vous allez recevoir une réponse 403 forbidden (interdit).

Étape 2 : Restreignez l’accès dans le gabarit

Vous pouvez aussi vérifier si un utilisateur a des permissions dans un gabarit. Cela peut vous servir pour montrer ou cacher du contenu en fonction de ses droits d’accès.

Ne montrons le lien   Télécharger une photo  que si l’utilisateur a la permission   blog.add_photo . Pour cela, utilisez l’attribut   perms  , qui est automatiquement chargé dans le contexte du gabarit :

# blog/templates/blog/base.html
…
{% if perms.blog.add_photo %}
    <a href="{% url 'photo_upload' %}">Upload a Photo</a>
{% endif %}

Et maintenant, si vous naviguez jusqu’à la page d’accueil :

Capture d'écran de la page d'accueil
Capture d'écran de la page d'accueil

Le lien a disparu.

L’accès à cette page est maintenant restreint en fonction des permissions.

Comment est-ce que je peux donner ces permissions aux utilisateurs ?

Avec du code, dans le shell Django.

Étape 3 : Donnez des permissions à un utilisateur

Vous pouvez utiliser la méthode  user_permissions.add()  pour ajouter des permissions.

>>> from authentication.models import User
>>> user = User.objects.get(username='toto')
>>> from django.contrib.auth.models import Permission
>>> permission = Permission.objects.get(codename='add_photo')
>>> user.user_permissions.add(permission)

 Si vous retournez au site, vous devriez voir réapparaître le lien  Télécharger une photo  , et pouvoir accéder à la page de mise en ligne de photos.

Capture d'écran de la page d'accueil
Capture d'écran de la page d'accueil

Ça fonctionne ! Un utilisateur a maintenant l’autorisation. Néanmoins, cette approche ne peut pas être mise à l’échelle si vous voulez l’appliquer à de nombreux utilisateurs. L’utilisation des groupes permet de résoudre ce problème.

Attribuez des permissions à plusieurs utilisateurs grâce aux groupes

À présent que le site a des parties restreintes en fonction des permissions, vous allez devoir en permettre l’accès à différents utilisateurs selon des spécifications.

Pour cela, répartissez les utilisateurs en groupes. Cela permet de regrouper un sous-ensemble d’utilisateurs. Ce groupement existe sous forme de tableau dans la base de données.

Pour notre site, nous aurons deux groupes :Creator (CréateuretSubscriber (Abonné). Nous voulons que ces groupes soient créés automatiquement. Ainsi, si quelqu’un récupérait une copie du projet et le paramétrait sur son ordinateur, il n’aurait pas besoin de créer ces groupes et de leur attribuer les permissions appropriées manuellement.

Pour ce faire, nous devons écrire une migration personnalisée.

Qu’est-ce que c’est, une migration personnalisée ?

Les migrations que vous avez rencontrées jusqu’à présent étaient liées à des changements du schéma dans la base de données, et étaient automatiquement générées par Django.

Les migrations personnalisées vous permettent de manipuler des données déjà contenues dans la base de données, ou même de créer de nouvelles instances de modèle fondées sur des critères spécifiques. Elles sont utiles si vous devez migrer des données vers un nouveau type de données, sans perdre aucune information.

Comme ce sont des migrations, elles peuvent être stockées dans votre historique de contrôle de version. Elles peuvent être récupérées et exécutées par toute personne ayant accès au projet. Cela permet à l’application d’être reproductible dans différents environnements. Elles seront également exécutées dès que quelqu’un configure le projet initialement.

Tout d’abord, nous allons écrire une migration personnalisée pour créer les groupes  creators  et   subscribers. Nous ajouterons ensuite les utilisateurs dans leur groupe approprié, en fonction de leur attribut  role  .  Découvrez plus de détails dans la vidéo qui suit :

Pour créer une migration personnalisée, vous devez générer une migration vide grâce au drapeau  --empty   . Ensuite, spécifiez l’application où la migration sera générée. Dans notre cas, c’est  authentication  :

python manage.py makemigrations --empty authentication

 Et maintenant, si vous regardez le répertoiremigrationsde l’application  authentication, vous verrez qu’une nouvelle migration a été générée. Voyons ce qu’elle contient :

# Generated by Django 3.2.1 on 2021-04-06 01:35

from django.db import migrations


class Migration(migrations.Migration):
    
    dependencies = [
        ('authentication', '0001_initial'),
    ]

    operations = [
    ]

 La propriété  dependencies   liste les migrations qui doivent être exécutées avant celle-ci, tandis qu’  operations  constitue une liste des opérations que la migration va effectuer.

Pour écrire le code sur mesure à exécuter dans la migration, vous devez l’écrire en tant que fonction prenant deux arguments,  apps  et   schema_editor  . Vous ne pouvez pas accéder aux modèles directement depuis les imports, vous devez donc utiliser la fonction  apps.get_model()  pour les récupérer.

Écrivons une fonction  create_groups  , qui va créer les groupes  creators  et   subscribers  , puis leur attribuer correctement les utilisateurs existants dans la base de données avec la fonction  Group.user_set.add()  :

def create_groups(apps, schema_migration):
    User = apps.get_model('authentication', 'User')
    Group = apps.get_model('auth', 'Group')
    Permission = apps.get_model('auth', 'Permission')

    add_photo = Permission.objects.get(codename='add_photo')
    change_photo = Permission.objects.get(codename='change_photo')
    delete_photo = Permission.objects.get(codename='delete_photo')
    view_photo = Permission.objects.get(codename='view_photo')
    
    creator_permissions = [
        add_photo,
        change_photo,
        delete_photo,
        view_photo,
    ]
    
    creators = Group(name='creators')
    creators.save()
    
creators.permissions.set(creator_permissions)

    subscribers = Group(name='subscribers')
    subscribers.save()
    subscribers.permissions.add(view_photo)
    
    for user in User.objects.all():
        if user.role == 'CREATOR':
            creators.user_set.add(user)
        if user.role == 'SUBSCRIBER':
            subscribers.user_set.add(user)

 Ensuite, ajoutez la migration aux  operations  en spécifiant la fonction  create_groups   comme argument de la classe  migrations.RunPython  :

class Migration(migrations.Migration):
    
        dependencies = [
    ('authentication', '0001_initial'),
    ]

    operations = [
        migrations.RunPython(create_groups)
    ]

Et maintenant, exécutez la migration :

(fotoblog) ~/fotoblog (master)
→ python manage.py migrate
Operations to perform:
    Apply all migrations: admin, auth, authentication, blog, contenttypes, sessions
Running migrations:
    Applying authentication.0002_auto_20210406_0135... OK

Ça a l’air d’avoir marché !

N’hésitez pas à aller sur votre site en tant que créateur connecté. Vous devriez voir la fonctionnalité  Upload Photo  apparaître à nouveau. Puis, visualisez la page en tant qu’abonné. Vous devriez constater que le bouton a disparu.

Maintenant que vous savez comment ajouter ces permissions, à vous de jouer pour construire le reste de la gestion des permissions !

C'est à vous ! Menez à bien la gestion des permissions

Maintenant que vous savez comment configurer les permissions pour la fonctionnalité  Photo Upload  , il est temps de construire les permissions restantes.

Quatre tâches vous attendent :

  1. Utilisez le décorateur   permission_required  pour restreindre l’accès aux vues    create_multiple_photos   ,   edit_blog  et   blog_and_photo_upload  .

  2. Utilisez l’attribut   perms  dans un gabarit pour afficher de façon optionnelle les liens   Télécharger plusieurs photos  ,  Écrire un billet  , et  Modifier un billet  .

  3. Mettez à jour la méthode   save()  de   User  pour ajouter l’utilisateur au bon groupe.

  4. Créez une migration personnalisée pour attribuer les bonnes permissions aux groupes pour le modèle  Blog  . Ce seront les mêmes permissions que pour   Photo  . 

Si vous bloquez sur un point ou que vous voulez vérifier votre implémentation, vous trouverez une solution dans le dépôt GitHub.

Configurez des accès à granularité fine avec les permissions personnalisées  

Les quatre autorisations par défaut créées par Django sont utiles, mais que faire si vous avez besoin de permissions à granularité plus fine? Vous pourriez vouloir, par exemple, qu’un type d’utilisateur soit uniquement capable de modifier un champ particulier dans un modèle, comme le champ  title  de  Blog  .

Comment faire ? En créant des permissions personnalisées ! La façon la plus simple de les créer est de les définir comme faisant partie du modèle.

Spécifiez des permissions personnalisées en configurant l’attribut  permissions  dans une classe  Meta  d’un modèle, comme ceci :

class Blog(models.Model):
    ...
    
    class Meta:
        permissions = [
            ('change_blog_title', 'Peut changer le titre d’un billet de blog')
        ]

Vous pouvez ensuite attribuer cette permission à des groupes ou des utilisateurs, exactement comme pour les permissions de base.

Les permissions par objet ne sont pas bien gérées par Django tel qu’il est conçu. Si vous avez besoin d’utiliser des permissions de cette façon, certaines bibliothèques tierces bien faites peuvent vous aider. Django Guardian et Rules sont deux solutions populaires à ce problème, et vous pouvez toujours aller voir la section sur les permissions de Django Packages pour d’autres propositions (les trois sont des ressources en anglais).

En résumé 

  • Django fournit quatre permissions par défaut pour chaque modèle, qui correspondent aux quatre opérations CRUD.

  • Vous pouvez utiliser le décorateur   permission_required  pour restreindre l’accès à une vue en fonction d’une permission.

  • Vous pouvez attribuer des permissions à des utilisateurs individuels, ou à plusieurs utilisateurs en utilisant les groupes.

  • Les migrations personnalisées vous permettent d’apporter des changements sur mesure à la base de données, qu’on peut répéter sur différents environnements..

  • Les permissions personnalisées peuvent être spécifiées dans une classe de modèle   Meta  .

  • Il vaut mieux utiliser un package tiers pour les permissions au niveau de l’objet.

Maintenant que vous savez restreindre l’accès en fonction des permissions et écrire des migrations personnalisées, vous pouvez aller découvrir les champs plusieurs-à-plusieurs (many-to-many en anglais).

Exemple de certificat de réussite
Exemple de certificat de réussite