0.13.23 : return the caller's conversations without opening private messages
Build release Docker image / Build Docker Images (push) Successful in 7m47s
Build release Docker image / Build Docker Images (push) Successful in 7m47s
La messagerie affichait des conversations vides : ni nom, ni interlocuteur, ni message. Le front peuplait `conversation` et ses `messages` via l'API REST, dont le sanitizer retire toute relation vers un content-type que le rôle ne peut pas lire — or ni `chat-conversation.find` ni `chat-message.find` ne sont accordées. Les accorder aurait suffi, mais aurait ouvert la lecture de TOUTES les conversations et de TOUS les messages privés à n'importe quel compte connecté. On ajoute donc une action dédiée : - GET /api/chat-conversation-members/mine — conversations du seul appelant, avec interlocuteurs et messages, renvoyées via ctx.send - mot de passe et jetons retirés des utilisateurs peuplés - permission `mine` déclarée dans permissions-sync, avec la raison pour laquelle les deux `find` restent volontairement absentes Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "harmony-back",
|
"name": "harmony-back",
|
||||||
"version": "0.13.22",
|
"version": "0.13.23",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "A Strapi application",
|
"description": "A Strapi application",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -2,6 +2,85 @@
|
|||||||
* chat-conversation-member controller
|
* chat-conversation-member controller
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { factories } from '@strapi/strapi'
|
import { factories } from "@strapi/strapi";
|
||||||
|
|
||||||
export default factories.createCoreController('api::chat-conversation-member.chat-conversation-member');
|
export default factories.createCoreController(
|
||||||
|
"api::chat-conversation-member.chat-conversation-member",
|
||||||
|
({ strapi }) => ({
|
||||||
|
/**
|
||||||
|
* Conversations de l'utilisateur courant, avec leurs interlocuteurs et
|
||||||
|
* leurs messages.
|
||||||
|
*
|
||||||
|
* Pourquoi une action dédiée plutôt que le `find` générique : le front
|
||||||
|
* peuplait `conversation` et ses `messages` à travers l'API REST, dont le
|
||||||
|
* sanitizer retire toute relation vers un content-type que le rôle n'a pas
|
||||||
|
* le droit de lire. Les conversations revenaient donc vides — ni nom, ni
|
||||||
|
* interlocuteur, ni message.
|
||||||
|
*
|
||||||
|
* Accorder `chat-conversation.find` et `chat-message.find` aurait suffi à
|
||||||
|
* lever le filtrage, mais aurait ouvert la lecture de **toutes** les
|
||||||
|
* conversations et de **tous** les messages privés à n'importe quel compte
|
||||||
|
* connecté. Cette action retourne les données via `ctx.send`, en les
|
||||||
|
* limitant à l'appelant.
|
||||||
|
*/
|
||||||
|
async mine(ctx) {
|
||||||
|
const userId = ctx.state.user?.id;
|
||||||
|
|
||||||
|
if (!userId) {
|
||||||
|
return ctx.unauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
const members = await strapi.db
|
||||||
|
.query("api::chat-conversation-member.chat-conversation-member")
|
||||||
|
.findMany({
|
||||||
|
where: { user: { id: userId } },
|
||||||
|
populate: {
|
||||||
|
user: true,
|
||||||
|
conversation: {
|
||||||
|
populate: {
|
||||||
|
users: { populate: { avatar: true } },
|
||||||
|
messages: {
|
||||||
|
populate: {
|
||||||
|
sender: { populate: { avatar: true } },
|
||||||
|
media: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Les entités portent le mot de passe et les jetons des utilisateurs
|
||||||
|
// peuplés : on ne renvoie jamais ces champs au navigateur.
|
||||||
|
const stripUser = (user: any) => {
|
||||||
|
if (!user) return user;
|
||||||
|
const {
|
||||||
|
password,
|
||||||
|
resetPasswordToken,
|
||||||
|
confirmationToken,
|
||||||
|
...safeUser
|
||||||
|
} = user;
|
||||||
|
return safeUser;
|
||||||
|
};
|
||||||
|
|
||||||
|
const sanitized = members.map((member: any) => ({
|
||||||
|
...member,
|
||||||
|
user: stripUser(member.user),
|
||||||
|
conversation: member.conversation
|
||||||
|
? {
|
||||||
|
...member.conversation,
|
||||||
|
users: (member.conversation.users ?? []).map(stripUser),
|
||||||
|
messages: (member.conversation.messages ?? []).map(
|
||||||
|
(message: any) => ({
|
||||||
|
...message,
|
||||||
|
sender: stripUser(message.sender),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
}));
|
||||||
|
|
||||||
|
ctx.send(sanitized);
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* Routes personnalisées de chat-conversation-member.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
method: "GET",
|
||||||
|
path: "/chat-conversation-members/mine",
|
||||||
|
handler: "chat-conversation-member.mine",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
@@ -68,6 +68,14 @@ const ROLE_PERMISSIONS: Record<string, string[]> = {
|
|||||||
"api::chat-conversation.chat-conversation.create",
|
"api::chat-conversation.chat-conversation.create",
|
||||||
"api::chat-conversation-member.chat-conversation-member.find",
|
"api::chat-conversation-member.chat-conversation-member.find",
|
||||||
"api::chat-conversation-member.chat-conversation-member.update",
|
"api::chat-conversation-member.chat-conversation-member.update",
|
||||||
|
// Conversations de l'appelant, avec interlocuteurs et messages.
|
||||||
|
// ⚠️ VOLONTAIREMENT ABSENTES : chat-conversation.find et
|
||||||
|
// chat-message.find. Elles lèveraient le filtrage du sanitizer sur les
|
||||||
|
// relations peuplées, mais ouvriraient la lecture de toutes les
|
||||||
|
// conversations et de tous les messages privés à n'importe quel compte
|
||||||
|
// connecté. L'action `mine` retourne les mêmes données, limitées à
|
||||||
|
// l'utilisateur courant.
|
||||||
|
"api::chat-conversation-member.chat-conversation-member.mine",
|
||||||
"api::chat-message.chat-message.create",
|
"api::chat-message.chat-message.create",
|
||||||
|
|
||||||
// --- Social (posts)
|
// --- Social (posts)
|
||||||
|
|||||||
Reference in New Issue
Block a user