App label :
messaging
Module :mayan.apps.messaging
Branche :egyptian(fork Mayan EDMS 4.11.1)
L'application messaging fournit un système de messagerie interne entre utilisateurs Mayan. Les messages sont privés : chaque message est adressé à un unique destinataire (user) et n'est visible que par lui. L'expéditeur peut être un utilisateur humain ou un objet système (via une clé étrangère générique sender_object).
Les cas d'usage principaux sont :
WorkflowActionMessageSend)MessageFichier : mayan/apps/messaging/models.py
| Champ | Type | Description |
|---|---|---|
sender_content_type |
FK → ContentType (nullable) |
Type Django du l'expéditeur (clé générique) |
sender_object_id |
PositiveIntegerField (nullable) | ID de l'objet expéditeur |
sender_object |
GenericForeignKey | Expéditeur réel (user, workflow, système…) |
user |
FK → AUTH_USER_MODEL |
Destinataire du message |
subject |
CharField(255) | Sujet court |
body |
TextField | Corps du message (HTML sanitisé à l'affichage) |
read |
BooleanField (défaut False) |
Lu / non lu |
date_time |
DateTimeField (auto_now_add) | Date de création |
Tri par défaut : -date_time (plus récent en premier).
MessageBusinessLogicMixin)Fichier : mayan/apps/messaging/model_mixins.py
| Méthode | Description |
|---|---|
get_label() |
Retourne un libellé lisible : <date> @<sender> "<subject>" |
get_clean_body() |
Sanitise le corps HTML : urlize() + nh3.clean() (supprime JS/attributs dangereux, ajoute nofollow noopener noreferrer) |
get_rendered_body() |
Rend le corps nettoyé via le moteur de template Django avec {'message': self} comme contexte |
mark_read(user) |
Passe read=True, déclenche l'événement message_edited |
mark_unread(user) |
Passe read=False, déclenche l'événement message_edited |
Note : Le corps affiché passe par
nh3.clean()avant tout rendu, ce qui bloque les injections XSS directes. Le template rendu reçoit uniquement{'message': self}.
Fichier : mayan/apps/messaging/permissions.py
Namespace : messaging
| Constante | Nom interne | Description |
|---|---|---|
permission_message_create |
message_create |
Créer un message |
permission_message_delete |
message_delete |
Supprimer un message |
permission_message_edit |
message_edit |
Modifier un message (marquer lu/non-lu) |
permission_message_view |
message_view |
Voir un message |
Les permissions sont enregistrées comme ACL objet sur le modèle Message (via ModelPermission.register). Un utilisateur ne voit que ses propres messages (request.user.messages.all()) — la requête de base est toujours filtrée par user.
Permissions ACL supplémentaires disponibles sur les objets Message : permission_acl_edit, permission_acl_view.
Fichier : mayan/apps/messaging/views.py
| Vue | URL | Permission | Description |
|---|---|---|---|
MessageListView |
GET /messaging/messages/ |
message_view |
Liste des messages du compte connecté |
MessageCreateView |
GET/POST /messaging/messages/create/ |
message_create |
Créer un message (expéditeur = utilisateur courant) |
MessageDetailView |
GET /messaging/messages/<id>/details/ |
message_view |
Détail d'un message — marque automatiquement le message comme lu à l'ouverture |
MessageDeleteView |
POST /messaging/messages/<id>/delete/ |
message_delete |
Supprimer un message |
MessageDeleteView (multiple) |
POST /messaging/messages/multiple/delete/ |
message_delete |
Suppression en masse |
MessageMarkReadView |
POST /messaging/messages/<id>/mark_read/ |
message_edit |
Marquer lu (simple ou multiple) |
MessageMarkUnReadView |
POST /messaging/messages/<id>/mark_unread/ |
message_edit |
Marquer non-lu (simple ou multiple) |
MessageMarkReadAllView |
POST /messaging/messages/all/mark_read/ |
message_edit |
Marquer tous les messages comme lus |
Le formulaire de création (MessageCreateForm) expose les champs user, subject, body. Le champ user est limité par get_user_queryset() (liste des utilisateurs accessibles selon les règles de visibilité configurées).
Fichier : mayan/apps/messaging/api_views.py
Préfixe : /api/v4/messages/
GET /api/v4/messages/Liste des messages du compte connecté.
Permission : message_view
POST /api/v4/messages/Créer un nouveau message.
Permission : message_create
Corps JSON :
{
"user": 42,
"subject": "Votre document a été approuvé",
"body": "Le document XYZ a été validé par le service RH."
}
Le champ
useraccepte un ID d'utilisateur (PrimaryKey). Le champbodyetsubjectne sont pas modifiables via PUT/PATCH (read-only en modification).
GET /api/v4/messages/<id>/Détail d'un message.
Permission : message_view
PUT / PATCH /api/v4/messages/<id>/Modifier un message. Seul le champ read est éditable en PUT/PATCH.
Permission : message_edit
DELETE /api/v4/messages/<id>/Supprimer un message.
Permission : message_delete
MessageSerializer| Champ | Accès | Description |
|---|---|---|
id |
lecture | ID interne |
url |
lecture | URL API du message |
user |
création | ID du destinataire |
subject |
création | Sujet |
body |
création | Corps |
read |
lecture/édition | Statut lu/non-lu |
date_time |
lecture | Date de création |
sender_app_label |
lecture | App label de l'expéditeur |
sender_model_name |
lecture | Nom de modèle de l'expéditeur |
sender_object_id |
lecture | ID de l'objet expéditeur |
sender_url |
lecture | URL API de l'expéditeur (si disponible) |
Fichier : mayan/apps/messaging/search.py
Le modèle Message est indexé dans le moteur de recherche dynamique.
| Champ indexé | Description |
|---|---|
date_time |
Date de création |
subject |
Sujet du message |
body |
Corps du message |
Permission de recherche : permission_message_view
La recherche est filtrée par ACL — un utilisateur ne retrouve que ses propres messages.
Exemple d'appel API de recherche :
GET /api/v4/messages/?subject=approbation
GET /api/v4/messages/?body=validé
WorkflowActionMessageSendFichier : mayan/apps/messaging/workflow_actions.py
Cette action de workflow permet d'envoyer automatiquement un message interne lors d'une transition d'état de document. Elle est enregistrée sous le label Send user message.
Tous les champs supportent les templates Django avec la variable workflow_instance disponible en contexte.
| Champ | Description | Exemple de template |
|---|---|---|
group_name_list |
Liste de noms de groupes séparés par virgule | direction,comptabilite |
role_name_list |
Liste de labels de rôles séparés par virgule | Responsable RH |
username_list |
Liste de noms d'utilisateurs séparés par virgule | alice,bob |
subject |
Sujet du message | Document {{ workflow_instance.document.label }} approuvé |
body |
Corps du message | Le document a été approuvé le {{ workflow_instance.date_time\|date:"d/m/Y" }}. |
username__in=final_username_list.Message.objects.create(body=body, subject=subject, user=user) est exécuté.sender_object) n'est pas défini dans cette action — les messages apparaissent sans expéditeur identifié.Action : Send user message
group_name_list : direction
role_name_list : (vide)
username_list : (vide)
subject : [{{ workflow_instance.document.document_type }}] Nouveau document à valider
body : Le document "{{ workflow_instance.document.label }}" (UUID: {{ workflow_instance.document.uuid }})
requiert votre validation. Soumis le {{ workflow_instance.date_time|date:"d/m/Y à H:i" }}.
Fichier : mayan/apps/messaging/events.py
Namespace événement : messaging
| Événement | Nom interne | Déclencheur |
|---|---|---|
event_message_created |
message_created |
Création d'un message (Message.save() première fois) |
event_message_edited |
message_edited |
Modification, mark_read, mark_unread |
L'événement event_message_edited est également déclenché lors des appels mark_read() et mark_unread(), permettant d'auditer les consultations de messages.
Configurées dans apps.py via SourceColumn :
| Colonne | Attribut | Triable | Lien |
|---|---|---|---|
| Date/heure | date_time |
Oui | Vers le détail |
| Expéditeur | sender_object |
Non | Vers l'objet expéditeur |
| Sujet | subject |
Oui | Vers le détail |
| Lu | read |
Oui | Indicateur visuel (TwoStateWidget) |
Création
├── Via interface : MessageCreateView → sender_object = request.user
├── Via API REST : POST /api/v4/messages/ → sender_object non défini
└── Via workflow : WorkflowActionMessageSend.execute() → sender_object non défini
↓
Message enregistré (read=False)
Événement : event_message_created
↓
Destinataire consulte le message
MessageDetailView → mark_read() automatique à l'ouverture
Événement : event_message_edited
↓
Actions disponibles :
├── mark_unread() → read=False, événement message_edited
├── mark_read() → read=True, événement message_edited
└── delete() → suppression en base
| Point | Détail |
|---|---|
| Isolation des messages | Chaque vue filtre par request.user.messages.all() — un utilisateur ne peut pas lire les messages d'un autre |
| Sanitisation HTML | get_clean_body() utilise nh3.clean() + urlize() avant tout affichage — protection XSS |
| Destinataire API | Le champ user utilise FilteredPrimaryKeyRelatedField avec source_queryset=get_user_queryset() — seuls les utilisateurs visibles sont adressables |
| Envoi workflow | WorkflowActionMessageSend n'applique aucun filtre ACL sur les destinataires résolus par nom de groupe/rôle/username — voir exploit_2_messaging_arbitrary_recipient.txt |
| Corps en lecture seule | En PUT/PATCH, body et subject sont read_only=True — impossible de modifier le contenu après création via l'API |
| Migration | Description |
|---|---|
0001_initial |
Création du modèle Message avec tous les champs |
0002_remove_message_parent |
Suppression du champ parent (réponses imbriquées abandonnées) |
0003_auto_20230116_0640 |
Ajustements automatiques (ordering, etc.) |