Protection route selon role

This commit is contained in:
Charles Mendiburu 2025-04-11 19:51:48 +02:00
parent f868effc9a
commit dcdcdee103
7 changed files with 231 additions and 75 deletions

View File

@ -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());
});
}}

View File

@ -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",

View File

@ -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",

View File

@ -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() {
<div>
<Header />
<Routes>
{/* Routes publiques */}
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/gestion" element={<ProtectedRoute element={<Gestion />} />} />
<Route path="/gestionObjets" element={<ProtectedRoute element={<ObjectManagement />} />} />
<Route path="/objet" element={<ProtectedRoute element={<Objet />} />} />
<Route path="/signup" element={<Signup />} />
<Route path="/login" element={<Login />} />
<Route path="/ajouterObjet" element={<ProtectedRoute element={<AddObject />} />} />
<Route path="/settings" element={<Settings />} />
<Route path="/sidebar" element={<Sidebar />} />
<Route path="/user" element={<User />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/adminobjet" element={<ProtectedRoute element={<AdminObjet />} />} />
{/* Routes protégées pour tous les utilisateurs connectés */}
<Route path="/gestion" element={<ProtectedRoute element={<Gestion />} allowedRoles={['admin', 'complexe', 'user']} />} />
<Route path="/gestionObjets" element={<ProtectedRoute element={<ObjectManagement />} allowedRoles={['admin', 'complexe', 'user']} />} />
<Route path="/objet" element={<ProtectedRoute element={<Objet />} allowedRoles={['admin', 'complexe', 'user']} />} />
{/* Routes protégées pour les admins et complexes */}
<Route path="/ajouterObjet" element={<ProtectedRoute element={<AddObject />} allowedRoles={['admin', 'complexe']} />} />
<Route path="/settings" element={<ProtectedRoute element={<Settings />} allowedRoles={['admin', 'complexe']} />} />
{/* Routes protégées pour tous les utilisateurs connectés */}
<Route path="/sidebar" element={<ProtectedRoute element={<Sidebar />} allowedRoles={['admin', 'complexe', 'user']} />} />
<Route path="/user" element={<ProtectedRoute element={<User />} allowedRoles={['admin', 'complexe', 'user']} />} />
{/* Routes protégées pour les admins uniquement */}
<Route path="/dashboard" element={<ProtectedRoute element={<Dashboard />} allowedRoles={['admin']} />} />
<Route path="/adminobjet" element={<ProtectedRoute element={<AdminObjet />} allowedRoles={['admin']} />} />
</Routes>
</div>
</Router>

View File

@ -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 (
<AuthContext.Provider value={{ token, login, logout }}>
<AuthContext.Provider value={{ token, user, login, logout }}>
{children}
</AuthContext.Provider>
);

View File

@ -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 <Navigate to="/login" />;
}
// 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 <Navigate to="/" />;
}
// 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;

View File

@ -8,6 +8,7 @@ function Home() {
const [activeFilter, setActiveFilter] = useState('all');
const [name, setName] = useState([]);
const { token, logout } = useAuth();
const { user } = useAuth();
return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-50">
@ -15,10 +16,13 @@ function Home() {
<div className="text-center mb-12">
<h2 className="text-4xl font-bold text-gray-900 mb-4">
Bienvenue dans ta ville intelligente.</h2>
{token ? (
<><h2>Tu es connecté</h2>
</>):(
{user ? (
<>
<h1>Bienvenue, {user.name} {user.surname}!</h1>
<p>Email : {user.sub}</p>
<p>Rôle : {user.role}</p>
</>
):(
<h2>Non connecté</h2>
)}
<p className="text-xl text-gray-600 max-w-3xl mx-auto">