ci: fix pipeline, update readme and anonymize ZT references

This commit is contained in:
2026-06-12 12:30:18 +02:00
commit d1bd1a9ba9
68 changed files with 10353 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
import { Request, Response, NextFunction } from 'express';
import { getUserById } from './userStore.js';
// ============================================================
// Auth Middleware — Session-based avec vérification passwordChangedAt
// ============================================================
/**
* Middleware d'authentification.
* Vérifie que l'utilisateur est connecté ET que sa session n'a pas été
* invalidée par un changement de mot de passe.
*/
const authMiddleware = (req: Request, res: Response, next: NextFunction) => {
const session = req.session as any;
if (!session.user || !session.user.id) {
return res.status(401).json({ error: "Non autorisé. Veuillez vous connecter." });
}
// Bloquer les APIs si changement de mot de passe requis
if (session.user.mustChangePassword && req.path !== '/api/change-password' && req.path !== '/api/logout') {
return res.status(403).json({ error: "Changement de mot de passe obligatoire.", mustChangePassword: true });
}
// Vérifier que le user existe encore et que le password n'a pas changé depuis le login
const user = getUserById(session.user.id);
if (!user) {
// User supprimé depuis le login → détruire la session
session.destroy(() => {});
return res.status(401).json({ error: "Session invalide. Veuillez vous reconnecter." });
}
// Comparer loginAt avec passwordChangedAt — si le password a changé après le login, invalider
if (session.user.loginAt && user.passwordChangedAt) {
const loginTime = new Date(session.user.loginAt).getTime();
const passwordChangeTime = new Date(user.passwordChangedAt).getTime();
if (passwordChangeTime > loginTime) {
session.destroy(() => {});
return res.status(401).json({ error: "Votre mot de passe a été modifié. Veuillez vous reconnecter." });
}
}
next();
};
/**
* Middleware admin.
* Doit être utilisé APRÈS authMiddleware (session.user déjà vérifié).
* Vérifie que l'utilisateur a le rôle admin.
*/
export const requireAdmin = (req: Request, res: Response, next: NextFunction) => {
const session = req.session as any;
if (!session.user || session.user.role !== 'admin') {
return res.status(403).json({ error: "Accès refusé. Droits administrateur requis." });
}
next();
};
/**
* Middleware de vue d'authentification.
* Identique à authMiddleware mais redirige vers /login au lieu de retourner du JSON.
*/
export const viewAuthMiddleware = (req: Request, res: Response, next: NextFunction) => {
const session = req.session as any;
if (!session.user || !session.user.id) {
return res.redirect('/login');
}
// Même vérification passwordChangedAt
const user = getUserById(session.user.id);
if (!user) {
session.destroy(() => {});
return res.redirect('/login');
}
if (session.user.loginAt && user.passwordChangedAt) {
const loginTime = new Date(session.user.loginAt).getTime();
const passwordChangeTime = new Date(user.passwordChangedAt).getTime();
if (passwordChangeTime > loginTime) {
session.destroy(() => {});
return res.redirect('/login');
}
}
next();
};
/**
* Middleware de vue exigeant le rôle admin.
* Redirige les non-admins vers /trending en déposant un cookie d'erreur.
*/
export const viewRequireAdmin = (req: Request, res: Response, next: NextFunction) => {
const session = req.session as any;
if (!session.user || session.user.role !== 'admin') {
res.cookie('authError', "Accès refusé. Droits administrateur requis.", { maxAge: 10000 });
return res.redirect('/trending');
}
next();
};
export default authMiddleware;