mirror of
				https://github.com/ae-utbm/sith.git
				synced 2025-11-04 02:53:06 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			266 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			266 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
## Un peu d'histoire
 | 
						|
 | 
						|
Par défaut, Django met à disposition un modèle `Group`,
 | 
						|
lié par clef étrangère au modèle `User`.
 | 
						|
Pour créer un système de gestion des groupes qui semblait plus
 | 
						|
approprié aux développeurs initiaux, un nouveau
 | 
						|
modèle [core.models.Group][]
 | 
						|
a été crée, et la relation de clef étrangère a été modifiée
 | 
						|
pour lier [core.models.User][] à ce dernier.
 | 
						|
 | 
						|
L'ancien modèle `Group` était implicitement
 | 
						|
divisé en deux catégories :
 | 
						|
 | 
						|
- les *méta-groupes* : groupes liés aux clubs et créés à la volée.
 | 
						|
  Ces groupes n'étaient liés par clef étrangère à aucun utilisateur.
 | 
						|
  Ils étaient récupérés à partir de leur nom uniquement
 | 
						|
  et étaient plus une indirection pour désigner l'appartenance à un club
 | 
						|
  que des groupes à proprement parler.
 | 
						|
- les *groupes réels* : groupes créés à la main 
 | 
						|
  et souvent hardcodés dans la configuration.
 | 
						|
 | 
						|
Cependant, ce nouveau système s'éloignait trop du cadre de Django
 | 
						|
et a fini par devenir une gêne.
 | 
						|
La vérification des droits lors des opérations est devenue
 | 
						|
une opération complexe et coûteuse en temps.
 | 
						|
 | 
						|
La gestion des groupes a donc été modifiée pour recoller un
 | 
						|
peu plus au cadre de Django.
 | 
						|
Toutefois, il n'a pas été tenté de revenir à 100%
 | 
						|
sur l'architecture prônée par Django.
 | 
						|
 | 
						|
D'une part, cela représentait un risque pour le succès de l'application 
 | 
						|
de la migration sur la base de données de production.
 | 
						|
 | 
						|
D'autre part, si une autre architecture a été tentée au début, 
 | 
						|
ce n'était pas sans raison :
 | 
						|
ce que nous voulons modéliser sur le site AE n'est pas
 | 
						|
complètement modélisable avec ce qu'offre Django.
 | 
						|
Il faut donc bien garder une surcouche au-dessus de l'authentification
 | 
						|
de Django.
 | 
						|
Tout le défi est de réussir à maintenir cette surcouche aussi fine
 | 
						|
que possible sans limiter ce que nous voulons faire.
 | 
						|
 | 
						|
## Représentation en base de données
 | 
						|
 | 
						|
Le modèle [core.models.Group][] a donc été légèrement remanié
 | 
						|
et la distinction entre groupes méta et groupes réels a été plus ou moins
 | 
						|
supprimée.
 | 
						|
La liaison de clef étrangère se fait toujours entre [core.models.User][]
 | 
						|
et [core.models.Group][].
 | 
						|
 | 
						|
Cependant, il y a une subtilité.
 | 
						|
Depuis le début, le modèle `Group` de django n'a jamais disparu.
 | 
						|
En effet, lorsqu'un modèle hérite d'un modèle qui n'est pas
 | 
						|
abstrait, Django garde les deux tables et les lie
 | 
						|
par une clef étrangère unique de clef primaire à clef primaire
 | 
						|
(pour plus de détail, lire 
 | 
						|
[la doc de django sur l'héritage de modèle](https://docs.djangoproject.com/fr/stable/topics/db/models/#model-inheritance))
 | 
						|
 | 
						|
L'organisation réelle de notre système de groupes
 | 
						|
est donc la suivante :
 | 
						|
<!-- J'ai utilisé un diagramme entité-relation
 | 
						|
 au lieu d'un diagramme de db, parce que Mermaid n'a que 
 | 
						|
 le diagramme entité-relation. -->
 | 
						|
 | 
						|
```mermaid
 | 
						|
---
 | 
						|
title: Représentation des groupes
 | 
						|
---
 | 
						|
erDiagram
 | 
						|
    core_user }o..o{ core_group: core_user_groups
 | 
						|
    auth_group }o..o{ auth_permission: auth_group_permissions
 | 
						|
    core_group ||--|| auth_group: ""
 | 
						|
    core_user }o..o{ auth_permission :"core_user_user_permissions"
 | 
						|
    
 | 
						|
    core_user {
 | 
						|
        int id PK
 | 
						|
        string username
 | 
						|
        string email
 | 
						|
        string first_name
 | 
						|
        etc etc
 | 
						|
    }
 | 
						|
    core_group {
 | 
						|
        int group_ptr_id PK,FK
 | 
						|
        string description
 | 
						|
        bool is_manually_manageable
 | 
						|
    }
 | 
						|
    auth_group {
 | 
						|
        int id PK
 | 
						|
        name string
 | 
						|
    }
 | 
						|
    auth_permission {
 | 
						|
        int id PK
 | 
						|
        string name
 | 
						|
    }
 | 
						|
```
 | 
						|
 | 
						|
Cette organisation, rajoute une certaine complexité,
 | 
						|
mais celle-ci est presque entièrement gérée par django,
 | 
						|
ce qui fait que la gestion n'est pas tellement plus compliquée
 | 
						|
du point de vue du développeur.
 | 
						|
 | 
						|
Chaque fois qu'un queryset implique notre `Group`
 | 
						|
ou le `Group` de django, l'autre modèle est automatiquement
 | 
						|
ajouté à la requête par jointure.
 | 
						|
De cette façon, on peut manipuler l'un ou l'autre,
 | 
						|
sans même se rendre que les tables sont dans des tables séparées.
 | 
						|
 | 
						|
Par exemple :
 | 
						|
 | 
						|
=== "python"
 | 
						|
 | 
						|
    ```python
 | 
						|
    from core.models import Group
 | 
						|
 | 
						|
    Group.objects.all()
 | 
						|
    ```
 | 
						|
 | 
						|
=== "SQL généré"
 | 
						|
 | 
						|
    ```sql
 | 
						|
    SELECT "auth_group"."id",
 | 
						|
           "auth_group"."name",
 | 
						|
           "core_group"."group_ptr_id",
 | 
						|
           "core_group"."is_manually_manageable",
 | 
						|
           "core_group"."description"
 | 
						|
    FROM "core_group"
 | 
						|
             INNER JOIN "auth_group" ON ("core_group"."group_ptr_id" = "auth_group"."id")
 | 
						|
    ```
 | 
						|
 | 
						|
!!!warning
 | 
						|
 | 
						|
    Django réussit à abstraire assez bien la logique relationnelle.
 | 
						|
    Cependant, gardez bien en mémoire que ce n'est pas quelque chose
 | 
						|
    de magique et que cette manière de faire a des limitations.
 | 
						|
    Par exemple, il devient impossible de `bulk_create`
 | 
						|
    des groupes.
 | 
						|
 | 
						|
 | 
						|
## La définition d'un groupe
 | 
						|
 | 
						|
Un groupe est constitué des informations suivantes :
 | 
						|
 | 
						|
- son nom : `name`
 | 
						|
- sa description : `description` (optionnelle)
 | 
						|
- si on autorise sa gestion par les utilisateurs du site : `is_manually_manageable`
 | 
						|
 | 
						|
Si un groupe est gérable manuellement, alors les administrateurs du site
 | 
						|
auront le droit d'assigner des utilisateurs à ce groupe depuis l'interface dédiée.
 | 
						|
 | 
						|
S'il n'est pas gérable manuellement, on cache aux utilisateurs du site
 | 
						|
la gestion des membres de ce groupe.
 | 
						|
La gestion se fait alors uniquement "sous le capot",
 | 
						|
de manière automatique lors de certains évènements.
 | 
						|
Par exemple, lorsqu'un utilisateur rejoint un club,
 | 
						|
il est automatiquement ajouté au groupe des membres
 | 
						|
du club.
 | 
						|
Lorsqu'il quitte le club, il est retiré du groupe.
 | 
						|
 | 
						|
## Les groupes utilisés
 | 
						|
 | 
						|
### Groupes principaux
 | 
						|
 | 
						|
Les groupes les plus notables gérables par les administrateurs du site sont :
 | 
						|
 | 
						|
- `Root` : administrateur global du site
 | 
						|
- `Accounting admin` : les administrateurs de la comptabilité
 | 
						|
- `Communication admin` : les administrateurs de la communication
 | 
						|
- `Counter admin` : les administrateurs des comptoirs (foyer et autre)
 | 
						|
- `SAS admin` : les administrateurs du SAS
 | 
						|
- `Forum admin` : les administrateurs du forum
 | 
						|
- `Pedagogy admin` : les administrateurs de la pédagogie (guide des UVs)
 | 
						|
 | 
						|
En plus de ces groupes, on peut noter :
 | 
						|
 | 
						|
- `Public` : tous les utilisateurs du site.
 | 
						|
  Un utilisateur est automatiquement ajouté à ce group
 | 
						|
  lors de la création de son compte.
 | 
						|
- `Subscribers` : tous les cotisants du site.
 | 
						|
  Les utilisateurs ne sont pas réellement ajoutés ce groupe ;
 | 
						|
  cependant, les utilisateurs cotisants sont implicitement
 | 
						|
  considérés comme membres du groupe lors de l'appel
 | 
						|
  à la méthode `User.has_perm`.
 | 
						|
- `Old subscribers` : tous les anciens cotisants.
 | 
						|
  Un utilisateur est automatiquement ajouté à ce groupe 
 | 
						|
  lors de sa première cotisation
 | 
						|
 | 
						|
!!!note "Utilisation du groupe Public"
 | 
						|
 | 
						|
    Le groupe Public est un groupe particulier.
 | 
						|
    Tout le monde faisant partie de ce groupe
 | 
						|
    (même les utilisateurs non-connectés en sont implicitement 
 | 
						|
    considérés comme membres),
 | 
						|
    il ne doit pas être utilisé pour résoudre les
 | 
						|
    permissions d'une vue.
 | 
						|
 | 
						|
    En revanche, il est utile pour attribuer une ressource
 | 
						|
    à tout le monde.
 | 
						|
    Par exemple, un produit avec le groupe de vente Public
 | 
						|
    est considéré comme achetable par tous utilisateurs.
 | 
						|
    S'il n'avait eu aucun group de vente, il n'aurait
 | 
						|
    été accessible à personne.
 | 
						|
 | 
						|
### Groupes de club
 | 
						|
 | 
						|
Chaque club est associé à deux groupes :
 | 
						|
le groupe des membres et le groupe du bureau.
 | 
						|
 | 
						|
Lorsqu'un utilisateur rejoint un club, il est automatiquement
 | 
						|
ajouté au groupe des membres.
 | 
						|
S'il rejoint le club en tant que membre du bureau,
 | 
						|
il est également ajouté au groupe du bureau.
 | 
						|
 | 
						|
Lorsqu'un utilisateur quitte le club, il est automatiquement
 | 
						|
retiré des groupes liés au club.
 | 
						|
S'il quitte le bureau, mais reste dans le club, 
 | 
						|
il est retiré du groupe du bureau, mais reste dans le groupe des membres.
 | 
						|
 | 
						|
### Groupes de ban
 | 
						|
 | 
						|
Les groupes de ban sont une catégorie de groupes à part,
 | 
						|
qui ne sont pas stockés dans la même table 
 | 
						|
et qui ne sont pas gérés sur la même interface
 | 
						|
que les autres groupes.
 | 
						|
 | 
						|
Les groupes de ban existants sont les suivants :
 | 
						|
 | 
						|
- `Banned from buying alcohol` : les utilisateurs interdits de vente d'alcool (non mineurs)
 | 
						|
- `Banned from counters` : les utilisateurs interdits d'utilisation des comptoirs
 | 
						|
- `Banned to subscribe` : les utilisateurs interdits de cotisation
 | 
						|
 | 
						|
## Groupes liés à une permission
 | 
						|
 | 
						|
Certaines actions sur le site demandent une permission en particulier,
 | 
						|
que l'on veut donner ou retirer n'importe quand.
 | 
						|
 | 
						|
Prenons par exemple les cotisations : lors de l'intégration,
 | 
						|
on veut permettre aux membres du bureau de l'Integ
 | 
						|
de créer des cotisations, et pareil pour les membres du bureau 
 | 
						|
de la Welcome Week pendant cette dernière.
 | 
						|
 | 
						|
Dans ces cas-là, il est pertinent de mettre à disposition
 | 
						|
des administrateurs du site une page leur permettant
 | 
						|
de gérer quels groupes ont une permission donnée.
 | 
						|
Pour ce faire, il existe 
 | 
						|
[PermissionGroupsUpdateView][core.views.PermissionGroupsUpdateView].
 | 
						|
 | 
						|
Pour l'utiliser, il suffit de créer une vue qui en hérite
 | 
						|
et de lui dire quelle est la permission dont on veut gérer
 | 
						|
les groupes :
 | 
						|
 | 
						|
```python
 | 
						|
from core.views.group import PermissionGroupsUpdateView
 | 
						|
 | 
						|
 | 
						|
class SubscriptionPermissionView(PermissionGroupsUpdateView):
 | 
						|
    permission = "subscription.add_subscription"
 | 
						|
```
 | 
						|
 | 
						|
Configurez l'url de la vue, et c'est tout !
 | 
						|
La page ainsi générée contiendra un formulaire
 | 
						|
avec un unique champ permettant de sélectionner des groupes.
 | 
						|
Par défaut, seuls les utilisateurs avec la permission
 | 
						|
`auth.change_permission` auront accès à ce formulaire
 | 
						|
(donc, normalement, uniquement les utilisateurs Root).
 |