diff --git a/Back-end/src/main/java/com/example/starter/AuthHandler.java b/Back-end/src/main/java/com/example/starter/AuthHandler.java index e3a31c3..2e62e0e 100644 --- a/Back-end/src/main/java/com/example/starter/AuthHandler.java +++ b/Back-end/src/main/java/com/example/starter/AuthHandler.java @@ -77,46 +77,53 @@ public class AuthHandler { } databaseService.pool - .preparedQuery("SELECT password,points FROM users WHERE email = ?") - .execute(Tuple.of(email)) - .onSuccess(result -> { - if (result.rowCount() == 0) { - context.response() - .setStatusCode(401) - .end(new JsonObject().put("error", "Email ou mot de passe incorrect").encode()); - return; - } + .preparedQuery("SELECT name, surname, password, points FROM users WHERE email = ?") // Ajout de name et surname + .execute(Tuple.of(email)) + .onSuccess(result -> { + if (result.rowCount() == 0) { + context.response() + .setStatusCode(401) + .end(new JsonObject().put("error", "Email ou mot de passe incorrect").encode()); + return; + } - String storedHashedPassword = result.iterator().next().getString("password"); - Integer nbPointsUser = result.iterator().next().getInteger("points"); + var row = result.iterator().next(); + String storedHashedPassword = row.getString("password"); + Integer nbPointsUser = row.getInteger("points"); + String name = row.getString("name"); + String surname = row.getString("surname"); - BCrypt.Result verification = BCrypt.verifyer().verify(password.toCharArray(), storedHashedPassword); + BCrypt.Result verification = BCrypt.verifyer().verify(password.toCharArray(), storedHashedPassword); - if (verification.verified) { - JsonObject claims = new JsonObject().put("sub", email); - if(nbPointsUser<=60){ - claims.put("role", "user"); - }else if(nbPointsUser<=100){ - claims.put("role", "complexe"); - }else if(nbPointsUser>=200){ - claims.put("role", "admin"); - } - - String token = jwtAuth.generateToken(claims); - context.response() - .setStatusCode(200) - .end(new JsonObject().put("token", token).encode()); - } else { - context.response() - .setStatusCode(401) - .end(new JsonObject().put("error", "Email ou mot de passe incorrect").encode()); - } - }) - .onFailure(err -> { - System.err.println("Erreur de connexion : " + err.getMessage()); - context.response() - .setStatusCode(500) - .end(new JsonObject().put("error", "Erreur serveur").encode()); - }); - } -} + if (verification.verified) { + JsonObject claims = new JsonObject() + .put("sub", email) + .put("name", name) + .put("surname", surname); + + if (nbPointsUser <= 60) { + claims.put("role", "user"); + } else if (nbPointsUser <= 100) { + claims.put("role", "complexe"); + } else if (nbPointsUser >= 200) { + claims.put("role", "admin"); + } + + String token = jwtAuth.generateToken(claims); + + context.response() + .setStatusCode(200) + .end(new JsonObject().put("token", token).encode()); + } else { + context.response() + .setStatusCode(401) + .end(new JsonObject().put("error", "Email ou mot de passe incorrect").encode()); + } + }) + .onFailure(err -> { + System.err.println("Erreur de connexion : " + err.getMessage()); + context.response() + .setStatusCode(500) + .end(new JsonObject().put("error", "Erreur serveur").encode()); + }); +}} \ No newline at end of file diff --git a/Front-end/package-lock.json b/Front-end/package-lock.json index d61485a..8494b4e 100644 --- a/Front-end/package-lock.json +++ b/Front-end/package-lock.json @@ -13,6 +13,7 @@ "@emotion/styled": "^11.14.0", "@mui/material": "^7.0.1", "axios": "^1.8.4", + "jwt-decode": "^4.0.0", "lucide-react": "^0.427.0", "react": "^18.3.1", "react-charts": "^3.0.0-beta.57", @@ -2070,7 +2071,6 @@ "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.4" } @@ -2819,6 +2819,71 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-abstract": { + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -3788,12 +3853,63 @@ "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", "license": "ISC" }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "license": "MIT" }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -4269,6 +4385,14 @@ "node": ">=4.0" } }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "engines": { + "node": ">=18" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", diff --git a/Front-end/package.json b/Front-end/package.json index ca037c9..36bfe68 100644 --- a/Front-end/package.json +++ b/Front-end/package.json @@ -14,6 +14,7 @@ "@emotion/styled": "^11.14.0", "@mui/material": "^7.0.1", "axios": "^1.8.4", + "jwt-decode": "^4.0.0", "lucide-react": "^0.427.0", "react": "^18.3.1", "react-charts": "^3.0.0-beta.57", diff --git a/Front-end/src/App.jsx b/Front-end/src/App.jsx index 18df2c9..c80fc95 100644 --- a/Front-end/src/App.jsx +++ b/Front-end/src/App.jsx @@ -14,7 +14,7 @@ import Sidebar from "./pages/Admin/sidebar.jsx"; import User from "./pages/Admin/User.jsx"; import Dashboard from "./pages/Admin/Dashboard.jsx"; import AdminObjet from "./pages/Admin/AdminObjet.jsx"; -import ProtectedRoute from './ProtectedRoute.jsx'; // Correction de l'import +import ProtectedRoute from "./ProtectedRoute.jsx"; function App() { return ( @@ -23,19 +23,28 @@ function App() {
+ {/* Routes publiques */} } /> } /> - } />} /> - } />} /> - } />} /> } /> } /> - } />} /> - } /> - } /> - } /> - } /> - } />} /> + + {/* Routes protégées pour tous les utilisateurs connectés */} + } allowedRoles={['admin', 'complexe', 'user']} />} /> + } allowedRoles={['admin', 'complexe', 'user']} />} /> + } allowedRoles={['admin', 'complexe', 'user']} />} /> + + {/* Routes protégées pour les admins et complexes */} + } allowedRoles={['admin', 'complexe']} />} /> + } allowedRoles={['admin', 'complexe']} />} /> + + {/* Routes protégées pour tous les utilisateurs connectés */} + } allowedRoles={['admin', 'complexe', 'user']} />} /> + } allowedRoles={['admin', 'complexe', 'user']} />} /> + + {/* Routes protégées pour les admins uniquement */} + } allowedRoles={['admin']} />} /> + } allowedRoles={['admin']} />} />
diff --git a/Front-end/src/AuthContext.jsx b/Front-end/src/AuthContext.jsx index ca5abc9..545e406 100644 --- a/Front-end/src/AuthContext.jsx +++ b/Front-end/src/AuthContext.jsx @@ -1,5 +1,6 @@ -// src/AuthContext.js import React, { createContext, useContext, useState, useEffect } from "react"; +import { jwtDecode } from "jwt-decode"; + // Créer le contexte const AuthContext = createContext(); @@ -7,22 +8,25 @@ const AuthContext = createContext(); // Hook pour accéder facilement au contexte export const useAuth = () => useContext(AuthContext); -// Fournisseur de contexte qui gère l'état du token +// Fournisseur de contexte qui gère l'état du token et de l'utilisateur export const AuthProvider = ({ children }) => { const [token, setToken] = useState(localStorage.getItem("token")); + const [user, setUser] = useState(null); - // Met à jour le token lorsque localStorage change + // Met à jour le token et décode l'utilisateur useEffect(() => { - const handleStorageChange = () => { - setToken(localStorage.getItem("token")); - }; - - window.addEventListener("storage", handleStorageChange); - - return () => { - window.removeEventListener("storage", handleStorageChange); - }; - }, []); + if (token) { + try { + const decoded = jwtDecode(token); + setUser(decoded); + } catch (error) { + console.error("Erreur lors du décodage du token:", error); + setUser(null); + } + } else { + setUser(null); + } + }, [token]); const login = (newToken) => { localStorage.setItem("token", newToken); @@ -32,10 +36,11 @@ export const AuthProvider = ({ children }) => { const logout = () => { localStorage.removeItem("token"); setToken(null); + setUser(null); }; return ( - + {children} ); diff --git a/Front-end/src/ProtectedRoute.jsx b/Front-end/src/ProtectedRoute.jsx index 9df0306..02d7308 100644 --- a/Front-end/src/ProtectedRoute.jsx +++ b/Front-end/src/ProtectedRoute.jsx @@ -1,16 +1,22 @@ -import { useAuth } from './AuthContext'; // Utilisation du contexte d'authentification -import { Navigate } from 'react-router-dom'; // Utilisation de React Router pour la redirection +import React from "react"; +import { Navigate } from "react-router-dom"; +import { useAuth } from "./AuthContext"; // Utilisation du contexte d'authentification -function ProtectedRoute({ element }) { - const { token } = useAuth(); // Vérifier si un token existe, donc si l'utilisateur est authentifié +function ProtectedRoute({ element, allowedRoles }) { + const { token, user } = useAuth(); // Vérifier si un token existe, donc si l'utilisateur est authentifié // Si l'utilisateur n'est pas authentifié, redirigez-le vers la page de login if (!token) { return ; } - // Si l'utilisateur est authentifié, permettez l'accès à la route + // Si l'utilisateur est authentifié mais n'a pas le bon rôle + if (allowedRoles && !allowedRoles.includes(user?.role)) { + return ; + } + + // Si l'utilisateur est authentifié et a les bons rôles, permettez l'accès à la route return element; } -export default ProtectedRoute; // Export de la fonction +export default ProtectedRoute; diff --git a/Front-end/src/pages/Home.jsx b/Front-end/src/pages/Home.jsx index 885b92b..a97f1d3 100644 --- a/Front-end/src/pages/Home.jsx +++ b/Front-end/src/pages/Home.jsx @@ -8,6 +8,7 @@ function Home() { const [activeFilter, setActiveFilter] = useState('all'); const [name, setName] = useState([]); const { token, logout } = useAuth(); + const { user } = useAuth(); return (
@@ -15,10 +16,13 @@ function Home() {

Bienvenue dans ta ville intelligente.

- {token ? ( - <>

Tu es connecté

- - ):( + {user ? ( + <> +

Bienvenue, {user.name} {user.surname}!

+

Email : {user.sub}

+

Rôle : {user.role}

+ + ):(

Non connecté

)}