Initial commit — specs pré-projet SatPRO + site de présentation (viewer, annotations, gestion des accès)
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
// Authentification : vérification des identifiants et émission/validation des JWT.
|
||||
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
import {
|
||||
JWT_SECRET,
|
||||
JWT_EXPIRATION_MINUTES,
|
||||
ADMIN_USER,
|
||||
ADMIN_PASSWORD,
|
||||
estAdmin
|
||||
} from './config.js';
|
||||
import { verifierConnexion, trouverUtilisateur } from './users.js';
|
||||
|
||||
/**
|
||||
* Vérifie les identifiants.
|
||||
* Ordre : d'abord l'ADMIN du .env, puis les comptes gérés (store users.json).
|
||||
*
|
||||
* Retourne un objet décrivant le résultat :
|
||||
* - { ok:true, login, role, admin, doitChangerMdp }
|
||||
* - { ok:false, raison } avec raison ∈ 'identifiants' | 'desactive'
|
||||
*/
|
||||
export async function verifierIdentifiants(login, motDePasse) {
|
||||
if (typeof login !== 'string' || typeof motDePasse !== 'string') {
|
||||
return { ok: false, raison: 'identifiants' };
|
||||
}
|
||||
|
||||
// 1) Compte administrateur (défini dans le .env, hors store).
|
||||
if (ADMIN_USER && login === ADMIN_USER) {
|
||||
if (motDePasse === ADMIN_PASSWORD) {
|
||||
return { ok: true, login, role: 'admin', admin: true, doitChangerMdp: false };
|
||||
}
|
||||
return { ok: false, raison: 'identifiants' };
|
||||
}
|
||||
|
||||
// 2) Comptes gérés via le site.
|
||||
const res = await verifierConnexion(login, motDePasse);
|
||||
if (res.ok) {
|
||||
return {
|
||||
ok: true,
|
||||
login: res.utilisateur.login,
|
||||
role: res.utilisateur.role,
|
||||
admin: false,
|
||||
doitChangerMdp: res.utilisateur.doitChangerMdp
|
||||
};
|
||||
}
|
||||
if (res.raison === 'desactive') {
|
||||
return { ok: false, raison: 'desactive' };
|
||||
}
|
||||
return { ok: false, raison: 'identifiants' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Émet un JWT signé. Le token ne porte que le login (`sub`) : le rôle est relu à
|
||||
* chaque requête depuis le store, pour que tout changement de rôle ou désactivation
|
||||
* décidé par l'admin prenne effet immédiatement, sans attendre une reconnexion.
|
||||
*/
|
||||
export function emettreJeton(login) {
|
||||
return jwt.sign({ sub: login }, JWT_SECRET, {
|
||||
expiresIn: `${JWT_EXPIRATION_MINUTES}m`
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Middleware Express : exige un JWT valide (Authorization: Bearer <token>).
|
||||
* Renseigne req.utilisateur = { login, role, admin }. Le rôle est relu en direct :
|
||||
* - admin du .env -> role 'admin' ;
|
||||
* - sinon rôle courant du store ; un compte supprimé ou désactivé est refusé (401/403).
|
||||
*/
|
||||
export async function exigerAuth(req, res, next) {
|
||||
const entete = req.headers.authorization || '';
|
||||
const [schema, jeton] = entete.split(' ');
|
||||
if (schema !== 'Bearer' || !jeton) {
|
||||
return res.status(401).json({ erreur: 'Authentification requise.' });
|
||||
}
|
||||
let charge;
|
||||
try {
|
||||
charge = jwt.verify(jeton, JWT_SECRET);
|
||||
} catch {
|
||||
return res.status(401).json({ erreur: 'Session invalide ou expirée.' });
|
||||
}
|
||||
|
||||
const login = charge.sub;
|
||||
if (estAdmin(login)) {
|
||||
req.utilisateur = { login, role: 'admin', admin: true };
|
||||
return next();
|
||||
}
|
||||
|
||||
// Rôle relu depuis le store à chaque requête.
|
||||
const u = await trouverUtilisateur(login);
|
||||
if (!u) {
|
||||
return res.status(401).json({ erreur: 'Compte introuvable.' });
|
||||
}
|
||||
if (u.role === 'desactive') {
|
||||
return res.status(403).json({ erreur: 'Ce compte est désactivé.' });
|
||||
}
|
||||
req.utilisateur = { login, role: u.role, admin: false };
|
||||
return next();
|
||||
}
|
||||
|
||||
/** Middleware : réservé à l'administrateur. */
|
||||
export function exigerAdmin(req, res, next) {
|
||||
if (!req.utilisateur?.admin) {
|
||||
return res.status(403).json({ erreur: 'Réservé à l’administrateur.' });
|
||||
}
|
||||
return next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Middleware : exige un rôle autorisé à contribuer (annoter).
|
||||
* Admin et contributeur passent ; consultation est refusé (403).
|
||||
*/
|
||||
export function exigerContributeur(req, res, next) {
|
||||
const role = req.utilisateur?.role;
|
||||
if (role === 'admin' || role === 'contributeur') {
|
||||
return next();
|
||||
}
|
||||
return res.status(403).json({ erreur: 'Votre accès est en lecture seule.' });
|
||||
}
|
||||
Reference in New Issue
Block a user