From bedb7bd4cc93a7dda9f56ae7660c2768471b752e Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Wed, 9 Jul 2025 23:22:01 +0000 Subject: [PATCH] Prevent admin users from accidentally demoting themselves or the last admin Implements server-side validation and updates client error messaging to prevent accidental role changes in `server/routes.ts` and `client/src/components/Administration.tsx`. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 91318273-c764-4fd4-be04-bdc12c38af32 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0715008c-7244-40f4-befc-26c014852236/9d9e1107-c416-4124-a939-a91c612c56a1.jpg --- attached_assets/image_1752103258756.png | Bin 0 -> 5750 bytes client/src/components/Administration.tsx | 9 ++++--- server/routes.ts | 33 ++++++++++++----------- 3 files changed, 24 insertions(+), 18 deletions(-) create mode 100644 attached_assets/image_1752103258756.png diff --git a/attached_assets/image_1752103258756.png b/attached_assets/image_1752103258756.png new file mode 100644 index 0000000000000000000000000000000000000000..29cefc6b41e9837501a6fccdc48cd33a87fc7711 GIT binary patch literal 5750 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 bGy7~fb)=rwZcqf+D+UbyGSG6} literal 0 HcmV?d00001 diff --git a/client/src/components/Administration.tsx b/client/src/components/Administration.tsx index 7d6f2c5..8a95a7b 100644 --- a/client/src/components/Administration.tsx +++ b/client/src/components/Administration.tsx @@ -136,9 +136,12 @@ export default function Administration() { toast({ title: "Utilisateur modifié avec succès" }); }, onError: (error) => { - const errorMessage = error.message.includes("Vous ne pouvez pas changer votre propre rôle") - ? "Vous ne pouvez pas changer votre propre rôle d'administrateur" - : "Impossible de modifier l'utilisateur"; + let errorMessage = "Impossible de modifier l'utilisateur"; + if (error.message.includes("Vous ne pouvez pas changer votre propre rôle")) { + errorMessage = "Vous ne pouvez pas changer votre propre rôle d'administrateur"; + } else if (error.message.includes("Impossible de modifier le rôle du dernier administrateur")) { + errorMessage = "Impossible de modifier le rôle du dernier administrateur"; + } toast({ title: "Erreur", description: errorMessage, variant: "destructive" }); }, }); diff --git a/server/routes.ts b/server/routes.ts index 77e75e7..7c4d3eb 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -294,24 +294,27 @@ export async function registerRoutes(app: Express): Promise { const currentUser = await storage.getUser(req.session.userId!); const userData = insertUserSchema.partial().parse(req.body); - // Protection : empêcher un admin de changer son propre rôle par accident - if (userId === currentUser?.id && userData.role && userData.role !== 'administrator') { - return res.status(400).json({ - error: "Vous ne pouvez pas changer votre propre rôle d'administrateur" - }); - } - - // Protection : ne pas permettre de changer le rôle du dernier administrateur - if (userData.role && userData.role !== 'administrator') { - const users = await storage.getAllUsers(); - const adminUsers = users.filter(u => u.role === 'administrator'); - const userToUpdate = users.find(u => u.id === userId); - - if (userToUpdate?.role === 'administrator' && adminUsers.length <= 1) { + // Protection spécifique pour le rôle seulement + if (userData.role) { + // Protection : empêcher un admin de changer son propre rôle par accident + if (userId === currentUser?.id && userData.role !== 'administrator') { return res.status(400).json({ - error: "Impossible de modifier le rôle du dernier administrateur" + error: "Vous ne pouvez pas changer votre propre rôle d'administrateur" }); } + + // Protection : ne pas permettre de changer le rôle du dernier administrateur + if (userData.role !== 'administrator') { + const users = await storage.getAllUsers(); + const adminUsers = users.filter(u => u.role === 'administrator'); + const userToUpdate = users.find(u => u.id === userId); + + if (userToUpdate?.role === 'administrator' && adminUsers.length <= 1) { + return res.status(400).json({ + error: "Impossible de modifier le rôle du dernier administrateur" + }); + } + } } const user = await storage.updateUser(userId, userData);