L'app acls implémente un système de contrôle d'accès par objet (object-level permissions). Là où les permissions globales s'appliquent à tous les objets d'un type, les ACLs permettent de définir des accès selon la formule :
« Accorder la permission X au rôle Y pour l'objet Z »
Exemple concret : le rôle Comptabilité peut voir les documents du cabinet Factures, mais pas ceux du cabinet RH.
AccessControlListChaque entrée ACL relie trois éléments via une GenericForeignKey (Django ContentTypes) :
| Champ | Type | Rôle |
|---|---|---|
content_type + object_id |
GenericForeignKey | L'objet cible (document, cabinet, index…) |
role |
FK → Role |
Le rôle qui reçoit l'accès |
permissions |
M2M → StoredPermission |
Les permissions accordées |
La contrainte unique_together (content_type, object_id, role) garantit une seule entrée ACL par couple objet/rôle.
ModelPermission — enregistrement des modèlesPour qu'un modèle soit protégeable par ACL, il doit être enregistré via ModelPermission.register() dans le apps.py de son app :
ModelPermission.register(
model=Document,
permissions=(
permission_document_view,
permission_document_edit,
permission_document_delete,
)
)
Cet appel fait trois choses automatiquement :
GenericRelation acls sur le modèleacl_created, acl_edited, acl_deletedUn objet peut hériter les permissions d'un objet parent via register_inheritance() :
ModelPermission.register_inheritance(
model=DocumentFile,
related='document'
)
Ainsi, si un rôle a accès à un Document, il hérite automatiquement de cet accès sur ses DocumentFile et DocumentFilePage — sans avoir à créer des ACLs séparées sur chaque niveau.
AccessControlListManager — le cœur du filtrageLe manager expose deux méthodes clés utilisées partout dans Mayan :
restrict_queryset(permission, queryset, user)Filtre un queryset pour ne retourner que les objets auxquels l'utilisateur a accès. Logique :
_get_acl_filters() pour ne garder que les objets avec une ACL accordant cette permission à un rôle de l'utilisateurcheck_access(obj, permission, user)Vérifie l'accès à un objet unique. Lève PermissionDenied si l'accès est refusé.
grant(permission, role, obj) / revoke(...)API programmatique pour accorder/révoquer une permission :
AccessControlList.objects.grant(
permission=permission_document_view,
role=role_comptabilite,
obj=cabinet_factures
)
_get_acl_filters()Le moteur de filtrage gère plusieurs niveaux de complexité :
| Cas | Description |
|---|---|
| 1 | Objet direct (ContentType simple) |
| 2 | Champ relationnel (FK vers un autre modèle) |
| 3 | GenericForeignKey (ContentType dynamique) |
| 4 | Objet direct avec héritage vers un parent |
| 5 | Champ relationnel avec héritage |
| 6 | GenericForeignKey avec héritage |
| 7 | Fonction de filtrage personnalisée (register_field_query_function) |
# Filtrer un queryset (usage typique dans une vue)
queryset = AccessControlList.objects.restrict_queryset(
permission=permission_document_view,
queryset=Document.objects.all(),
user=request.user
)
# Vérifier l'accès à un objet unique
AccessControlList.objects.check_access(
obj=document,
permission=permission_document_edit,
user=request.user
)
| Fichier | Rôle |
|---|---|
models.py |
Modèle AccessControlList (ContentType + Role + Permissions) |
managers.py |
AccessControlListManager — filtrage et vérification d'accès |
classes.py |
ModelPermission — registre des modèles et héritage |
model_mixins.py |
Logique métier (permissions_add, permissions_remove) |
workflow_actions.py |
Action de workflow pour accorder/révoquer des ACLs automatiquement |