fix(sécurité): réserver les actions des Paramètres aux administrateurs et masquer les secrets

Les actions serveur de src/features/settings/actions.ts étaient appelables
par n'importe quel utilisateur connecté : la restriction de /settings dans le
middleware ne protège pas une action serveur. Un simple utilisateur pouvait
lire l'URL PostgreSQL, le mot de passe Qlik et les clés IA, réécrire la
configuration (puis refreshDb()), ou faire appeler une URL arbitraire au
serveur via les boutons « Tester ».

- requireAdmin() partagé dans src/lib/auth.ts, appelé en tête de chaque
  action des Paramètres ; il remplace les deux copies locales d'ensureAdmin()
  de user-actions.ts et api-key-actions.ts.
- getSavedDatabaseConfig ne renvoie plus aucun secret : URL sans mot de
  passe, mot de passe Qlik et clés IA remplacés par des booléens has*.
- Un mot de passe laissé vide conserve celui enregistré (enregistrement et
  test), uniquement pour le même hôte/utilisateur afin qu'il ne puisse pas
  être envoyé à un autre serveur.
- saveDatabaseSettings n'écrase plus les clés et modèles IA quand la page
  n'envoie que l'URL (ils étaient effacés à chaque sauvegarde).
- Le diagnostic de l'API FF passe dans lib/api-ff-client (diagnoseFfApi) :
  /api/ff-status reste accessible à tous sans exposer l'action admin.
- Le mot de passe PostgreSQL n'est plus persisté dans le localStorage.

Les routes /api/admin/** et sync-settings.ts (module serveur, pas une
action) étaient déjà correctement protégées : inchangées.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RkfUui3qGg2zmu3uHWjNXJ
This commit is contained in:
Claude committed 2026-10-04 06:35:35 +00:00
1 parent 2c84cfda53
commit af12527731
8 files changed
+205 -96

No files matched your search

+15
View File
@@ -70,3 +70,18 @@ export const { handlers, auth, signIn, signOut } = NextAuth({
})
],
});
/**
* Garde des actions serveur réservées aux administrateurs.
*
* Une action serveur est appelable directement par n'importe quel client connecté :
* la restriction de `/settings` dans le middleware ne la protège pas. Lève si la
* session n'est pas admin et renvoie la session sinon.
*/
export async function requireAdmin() {
const session = await auth();
if ((session?.user as { role?: string } | undefined)?.role !== "admin") {
throw new Error("Accès refusé : Droits administrateur requis.");
}
return session;
}