Skip to content

Commit bb517e6

Browse files
authored
Merge pull request #114 from RustyRory/feat/111-authentification-connexion
feat(auth): Fixes #111 - Connexion user
2 parents edcb92d + 00a1b16 commit bb517e6

16 files changed

Lines changed: 362 additions & 9 deletions

File tree

app/backend/src/app.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import championshipsRoutes from './routes/championships.js';
3030
import standingsRoutes from './routes/standings.js';
3131
import matchesRoutes from './routes/matches.js';
3232
import authRoutes from './routes/auth.js';
33+
import adminRoutes from './routes/admin.js';
3334

3435
// Utiliser les routes existantes
3536
app.use('/api/users', usersRoutes);
@@ -45,6 +46,7 @@ app.use('/api/championships', championshipsRoutes);
4546
app.use('/api/standings', standingsRoutes);
4647
app.use('/api/matches', matchesRoutes);
4748
app.use('/auth', authRoutes);
49+
app.use('/admin', adminRoutes);
4850

4951
// 🔹 Nouveaux endpoints pour récupérer les matchs
5052
import Match from './models/Match.js';
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import jwt from 'jsonwebtoken';
2+
import User from '../models/User.js';
3+
4+
/**
5+
* Middleware d'authentification
6+
* Vérifie le token JWT et attache l'utilisateur à req.user
7+
*/
8+
export const authMiddleware = async (req, res, next) => {
9+
try {
10+
const authHeader = req.headers.authorization;
11+
12+
// Vérification présence header
13+
if (!authHeader || !authHeader.startsWith('Bearer ')) {
14+
return res.status(401).json({ message: 'Token manquant ou invalide' });
15+
}
16+
17+
const token = authHeader.split(' ')[1];
18+
19+
// Vérification du token
20+
const decoded = jwt.verify(token, process.env.JWT_SECRET);
21+
22+
// Récupération utilisateur
23+
const user = await User.findById(decoded.id).select('-passwordHash');
24+
25+
if (!user) {
26+
return res.status(401).json({ message: 'Utilisateur introuvable' });
27+
}
28+
29+
// Compte désactivé
30+
if (!user.isActive) {
31+
return res.status(403).json({ message: 'Compte désactivé' });
32+
}
33+
34+
// Injection user dans la requête
35+
req.user = user;
36+
next();
37+
} catch (err) {
38+
console.error('AUTH MIDDLEWARE ERROR:', err);
39+
return res.status(401).json({ message: 'Token invalide ou expiré' });
40+
}
41+
};
42+
43+
/**
44+
* Middleware de rôle
45+
* @param {...string} roles autorisés
46+
*/
47+
export const requireRole = (...roles) => {
48+
return (req, res, next) => {
49+
if (!req.user) {
50+
return res.status(401).json({ message: 'Non authentifié' });
51+
}
52+
53+
if (!roles.includes(req.user.role)) {
54+
return res.status(403).json({ message: 'Accès refusé' });
55+
}
56+
57+
next();
58+
};
59+
};

app/backend/src/routes/admin.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import express from 'express';
2+
import User from '../models/User.js';
3+
import { authMiddleware, requireRole } from '../middlewares/authMiddleware.js';
4+
5+
const router = express.Router();
6+
7+
/**
8+
* GET /admin/users
9+
* Liste tous les utilisateurs
10+
*/
11+
router.get('/users', authMiddleware, requireRole('admin'), async (req, res) => {
12+
try {
13+
const users = await User.find().select('-passwordHash').sort({ createdAt: -1 });
14+
res.json(users);
15+
} catch (error) {
16+
console.error('GET USERS ERROR:', error);
17+
res.status(500).json({ message: 'Erreur serveur' });
18+
}
19+
});
20+
21+
/**
22+
* PATCH /admin/users/:id/activate
23+
* Activer / désactiver un compte utilisateur
24+
*/
25+
router.patch('/users/:id/activate', authMiddleware, requireRole('admin'), async (req, res) => {
26+
try {
27+
const { isActive } = req.body;
28+
29+
const user = await User.findById(req.params.id);
30+
if (!user) {
31+
return res.status(404).json({ message: 'Utilisateur introuvable' });
32+
}
33+
34+
user.isActive = isActive;
35+
await user.save();
36+
37+
res.json({
38+
message: `Compte ${isActive ? 'activé' : 'désactivé'}`,
39+
});
40+
} catch (error) {
41+
console.error('ACTIVATE USER ERROR:', error);
42+
res.status(500).json({ message: 'Erreur serveur' });
43+
}
44+
});
45+
46+
/**
47+
* PATCH /admin/users/:id/role
48+
* Modifier le rôle d’un utilisateur
49+
*/
50+
router.patch('/users/:id/role', authMiddleware, requireRole('admin'), async (req, res) => {
51+
try {
52+
const { role } = req.body;
53+
const allowedRoles = ['admin', 'editor', 'user'];
54+
55+
if (!allowedRoles.includes(role)) {
56+
return res.status(400).json({ message: 'Rôle invalide' });
57+
}
58+
59+
const user = await User.findById(req.params.id);
60+
if (!user) {
61+
return res.status(404).json({ message: 'Utilisateur introuvable' });
62+
}
63+
64+
user.role = role;
65+
await user.save();
66+
67+
res.json({
68+
message: 'Rôle mis à jour',
69+
});
70+
} catch (error) {
71+
console.error('UPDATE ROLE ERROR:', error);
72+
res.status(500).json({ message: 'Erreur serveur' });
73+
}
74+
});
75+
76+
export default router;

app/backend/src/routes/auth.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import express from 'express';
22
import jwt from 'jsonwebtoken';
33
import User from '../models/User.js';
44

5+
import { authMiddleware } from '../middlewares/authMiddleware.js';
6+
57
const router = express.Router();
68

79
/**
@@ -102,4 +104,24 @@ router.post('/login', async (req, res) => {
102104
}
103105
});
104106

107+
/**
108+
* GET /auth/me
109+
* Retourne l'utilisateur connecté
110+
*/
111+
router.get('/me', authMiddleware, async (req, res) => {
112+
try {
113+
res.json({
114+
id: req.user._id,
115+
email: req.user.email,
116+
role: req.user.role,
117+
firstName: req.user.firstName,
118+
lastName: req.user.lastName,
119+
createdAt: req.user.createdAt,
120+
});
121+
} catch (error) {
122+
console.error('AUTH ME ERROR:', error);
123+
res.status(500).json({ message: 'Erreur serveur' });
124+
}
125+
});
126+
105127
export default router;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import mongoose from 'mongoose';
2+
import dotenv from 'dotenv';
3+
import User from '../models/User.js';
4+
5+
dotenv.config();
6+
7+
const seedAdmin = async () => {
8+
try {
9+
// Connexion à MongoDB
10+
await mongoose.connect(process.env.MONGO_URI);
11+
12+
console.log('✅ Connecté à MongoDB');
13+
14+
// Vérifier s'il existe déjà un admin
15+
const existingAdmin = await User.findOne({ role: 'admin' });
16+
17+
if (existingAdmin) {
18+
console.log('⚠️ Un admin existe déjà :', existingAdmin.email);
19+
process.exit(0);
20+
}
21+
22+
// Création de l'admin
23+
const admin = new User({
24+
email: process.env.ADMIN_EMAIL,
25+
firstName: 'Admin',
26+
lastName: 'Root',
27+
role: 'admin',
28+
isActive: true,
29+
});
30+
31+
await admin.setPassword(process.env.ADMIN_PASSWORD);
32+
33+
await admin.save();
34+
35+
console.log('🎉 Admin créé avec succès');
36+
console.log('📧 Email:', admin.email);
37+
console.log('🔑 Mot de passe:', process.env.ADMIN_PASSWORD);
38+
39+
process.exit(0);
40+
} catch (err) {
41+
console.error('❌ Erreur seed admin:', err);
42+
process.exit(1);
43+
}
44+
};
45+
46+
seedAdmin();

docs/README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ Chaque dossier correspond à une étape clé du cycle projet, de l’idée au co
148148

149149
## 7. Backend
150150

151-
**[docs/data-conception/backend](/docs/project/backend.md/)**
151+
**[docs/backend](/docs/project/backend.md/)**
152152

153153
### Contenu
154154
- Installation et configuration de Node.js et Express
@@ -165,7 +165,13 @@ Chaque dossier correspond à une étape clé du cycle projet, de l’idée au co
165165
- Utiliser MongoDB de manière flexible, avec création automatique des bases et collections à l’insertion de documents
166166
- Permettre le développement rapide grâce à nodemon et à une configuration simple des routes et contrôleurs
167167

168-
**[docs/data-conception/scraping](/docs/project/scraping.md/)**
168+
**[docs/scraping](/docs/project/scraping.md/)**
169+
170+
### Contenu
171+
172+
### Objectifs
173+
174+
**[docs/authentification](/docs/project/auth.md/)**
169175

170176
### Contenu
171177

0 commit comments

Comments
 (0)