Compare commits

..

2 Commits

Author SHA1 Message Date
dahoud
13d3097b3e Refactoring 2026-02-07 17:04:49 +00:00
dahoud
fc451f025e fix(security): Correction definitive de la verification JWT HS256
PROBLEME RESOLU:

- Les tokens JWT generes au login n'etaient pas verifies correctement

- SmallRye JWT ne pouvait pas charger la cle de verification

- Incompatibilite entre l'issuer du token et celui attendu

CORRECTIONS:

- Creation de jwt-secret.jwk au format JWK standard pour cles symetriques

- Configuration smallrye.jwt.verify.key.location vers le fichier JWK

- Alignement de l'issuer sur 'afterwork' dans .env.example

Ce commit sert de checkpoint stable pour la configuration JWT.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-07 02:44:35 +00:00
9 changed files with 69 additions and 40 deletions

View File

@@ -37,7 +37,8 @@ DB_PASSWORD=skyfile
# Générez avec: openssl rand -base64 32
JWT_SECRET=afterwork-jwt-secret-min-32-bytes-for-hs256!
JWT_LIFESPAN=86400
JWT_ISSUER=afterwork-api
# IMPORTANT: L'issuer doit être "afterwork" (correspondant à JwtService.ISSUER)
JWT_ISSUER=afterwork
# ============================================
# SUPER ADMIN

View File

@@ -88,8 +88,8 @@ public class RolePermissionConfig {
PROFILE_READ, PROFILE_UPDATE, PROFILE_DELETE,
// Social / Recherche
SOCIAL_SEARCH, SOCIAL_FOLLOW, SOCIAL_BLOCK,
// Événements
EVENTS_READ, EVENTS_PARTICIPATE,
// Événements - lecture, participation et création
EVENTS_READ, EVENTS_PARTICIPATE, EVENTS_CREATE, EVENTS_UPDATE_OWN, EVENTS_DELETE_OWN,
// Établissements
ESTABLISHMENTS_READ,
// Réservations
@@ -137,8 +137,8 @@ public class RolePermissionConfig {
// ===== ESTABLISHMENT_MANAGER (Gérant / Manager) =====
rolePermissions.put(ESTABLISHMENT_MANAGER, EnumSet.of(
// Établissement - gestion partielle
ESTABLISHMENTS_UPDATE_OWN,
// Établissement - gestion partielle + création
ESTABLISHMENTS_CREATE, ESTABLISHMENTS_UPDATE_OWN,
// Événements - gestion pour l'établissement
EVENTS_CREATE, EVENTS_UPDATE_OWN, EVENTS_DELETE_OWN,
// Réservations - gestion

View File

@@ -31,7 +31,7 @@ import java.util.concurrent.ConcurrentHashMap;
*
* URL: ws://localhost:8080/chat/{userId}
*/
@WebSocket(path = "/chat/{userId}")
@WebSocket(path = "/afterwork/chat/{userId}")
@ApplicationScoped
public class ChatWebSocketNext {

View File

@@ -30,7 +30,7 @@ import java.util.concurrent.ConcurrentHashMap;
*
* URL: ws://localhost:8080/notifications/{userId}
*/
@WebSocket(path = "/notifications/{userId}")
@WebSocket(path = "/afterwork/notifications/{userId}")
@ApplicationScoped
public class NotificationWebSocketNext {

View File

@@ -0,0 +1,6 @@
{
"kty": "oct",
"k": "YWZ0ZXJ3b3JrLWp3dC1zZWNyZXQtbWluLTMyLWJ5dGVzLWZvci1oczI1NiE",
"alg": "HS256",
"use": "sig"
}

View File

@@ -40,7 +40,9 @@ quarkus.flyway.clean-at-start=false
# ====================================================================
# drop-and-create: recrée le schéma à chaque démarrage (pratique en dev).
# ATTENTION: Ne JAMAIS utiliser ce mode en production !
quarkus.hibernate-orm.database.generation=drop-and-create
# Utiliser 'update' pour conserver les données entre les redémarrages
# Changer en 'drop-and-create' uniquement si vous voulez réinitialiser la base
quarkus.hibernate-orm.database.generation=update
quarkus.hibernate-orm.log.sql=true
quarkus.hibernate-orm.log.format-sql=true
quarkus.hibernate-orm.packages=com.lions.dev.entity

View File

@@ -35,30 +35,29 @@ smallrye.jwt.new-token.lifespan=${JWT_LIFESPAN:86400}
smallrye.jwt.new-token.issuer=afterwork
# ====================================================================
# JWT Configuration (SmallRye JWT)
# JWT Configuration (SmallRye JWT avec HS256)
# ====================================================================
# Algorithme de signature/vérification (symétrique HS256)
# Algorithme de signature/vérification : HMAC-SHA256 (symétrique)
smallrye.jwt.verify.algorithm=HS256
mp.jwt.verify.issuer=afterwork
# Clé secrète pour vérifier les tokens HS256 (même valeur que afterwork.jwt.secret)
# SmallRye JWT supporte les clés symétriques via cette propriété
smallrye.jwt.verify.key.location=META-INF/jwt-secret.key
# Clé secrète HS256 au format JWK (JSON Web Key)
# IMPORTANT: La valeur "k" est la clé afterwork.jwt.secret encodée en Base64URL
smallrye.jwt.verify.key.location=jwt-secret.jwk
# Activer la propagation du token pour @RolesAllowed
quarkus.smallrye-jwt.blocking-authentication=true
# Désactiver l'authentification proactive (laisser les endpoints publics accessibles)
quarkus.http.auth.proactive=false
# ====================================================================
# Sécurité HTTP - Permissions par chemin
# ====================================================================
# Endpoints publics (sans authentification requise)
quarkus.http.auth.permission.public.paths=/afterwork/users/register,/afterwork/users/authenticate,/afterwork/users/forgot-password,/afterwork/users/reset-password,/afterwork/q/*,/afterwork/openapi,/afterwork/webhooks/*
quarkus.http.auth.permission.public.paths=/afterwork/users/register,/afterwork/users/authenticate,/afterwork/users/forgot-password,/afterwork/users/reset-password,/afterwork/q/*,/afterwork/openapi,/afterwork/webhooks/*,/afterwork/media/files/*,/q/*
quarkus.http.auth.permission.public.policy=permit
# Endpoints admin (SUPER_ADMIN ou ADMIN requis)
quarkus.http.auth.permission.admin.paths=/afterwork/admin/*
quarkus.http.auth.permission.admin.policy=authenticated
quarkus.http.auth.permission.admin.roles=SUPER_ADMIN,ADMIN
# Tous les autres endpoints requièrent une authentification
quarkus.http.auth.permission.authenticated.paths=/afterwork/*

View File

@@ -0,0 +1,5 @@
{
"kty": "oct",
"k": "YWZ0ZXJ3b3JrLWp3dC1zZWNyZXQtbWluLTMyLWJ5dGVzLWZvci1oczI1NiE",
"alg": "HS256"
}

View File

@@ -26,6 +26,9 @@ class SecurityServiceTest {
@Inject
SecurityService securityService;
@Inject
RolePermissionConfig rolePermissionConfig;
// =========================================================================
// Tests des permissions par rôle
// =========================================================================
@@ -33,7 +36,7 @@ class SecurityServiceTest {
@Test
@DisplayName("Role USER devrait avoir les permissions de base")
void testUserRoleHasBasicPermissions() {
Set<Permission> userPermissions = RolePermissionConfig.getPermissions(Role.USER);
Set<Permission> userPermissions = rolePermissionConfig.getPermissions(Role.USER);
// Vérifier les permissions de profil
assertTrue(userPermissions.contains(Permission.PROFILE_READ), "USER devrait pouvoir lire son profil");
@@ -53,27 +56,27 @@ class SecurityServiceTest {
}
@Test
@DisplayName("Role OWNER devrait avoir les permissions d'établissement")
@DisplayName("Role ESTABLISHMENT_OWNER devrait avoir les permissions d'établissement")
void testOwnerRoleHasEstablishmentPermissions() {
Set<Permission> ownerPermissions = RolePermissionConfig.getPermissions(Role.OWNER);
Set<Permission> ownerPermissions = rolePermissionConfig.getPermissions(Role.ESTABLISHMENT_OWNER);
// Permissions de gestion d'établissement
assertTrue(ownerPermissions.contains(Permission.ESTABLISHMENTS_CREATE), "OWNER devrait pouvoir créer un établissement");
assertTrue(ownerPermissions.contains(Permission.ESTABLISHMENTS_UPDATE_OWN), "OWNER devrait pouvoir modifier son établissement");
assertTrue(ownerPermissions.contains(Permission.ESTABLISHMENTS_DELETE_OWN), "OWNER devrait pouvoir supprimer son établissement");
assertTrue(ownerPermissions.contains(Permission.ESTABLISHMENTS_MANAGE_STAFF), "OWNER devrait pouvoir gérer le personnel");
assertTrue(ownerPermissions.contains(Permission.ESTABLISHMENTS_VIEW_ANALYTICS), "OWNER devrait voir les analytics");
assertTrue(ownerPermissions.contains(Permission.ESTABLISHMENTS_CREATE), "ESTABLISHMENT_OWNER devrait pouvoir créer un établissement");
assertTrue(ownerPermissions.contains(Permission.ESTABLISHMENTS_UPDATE_OWN), "ESTABLISHMENT_OWNER devrait pouvoir modifier son établissement");
assertTrue(ownerPermissions.contains(Permission.ESTABLISHMENTS_DELETE_OWN), "ESTABLISHMENT_OWNER devrait pouvoir supprimer son établissement");
assertTrue(ownerPermissions.contains(Permission.ESTABLISHMENTS_MANAGE_STAFF), "ESTABLISHMENT_OWNER devrait pouvoir gérer le personnel");
assertTrue(ownerPermissions.contains(Permission.ESTABLISHMENTS_VIEW_ANALYTICS), "ESTABLISHMENT_OWNER devrait voir les analytics");
// Permissions de promotions
assertTrue(ownerPermissions.contains(Permission.PROMOTIONS_CREATE), "OWNER devrait pouvoir créer des promotions");
assertTrue(ownerPermissions.contains(Permission.PROMOTIONS_UPDATE_OWN), "OWNER devrait pouvoir modifier ses promotions");
assertTrue(ownerPermissions.contains(Permission.PROMOTIONS_DELETE_OWN), "OWNER devrait pouvoir supprimer ses promotions");
assertTrue(ownerPermissions.contains(Permission.PROMOTIONS_CREATE), "ESTABLISHMENT_OWNER devrait pouvoir créer des promotions");
assertTrue(ownerPermissions.contains(Permission.PROMOTIONS_UPDATE_OWN), "ESTABLISHMENT_OWNER devrait pouvoir modifier ses promotions");
assertTrue(ownerPermissions.contains(Permission.PROMOTIONS_DELETE_OWN), "ESTABLISHMENT_OWNER devrait pouvoir supprimer ses promotions");
}
@Test
@DisplayName("Role SUPER_ADMIN devrait avoir toutes les permissions")
void testSuperAdminHasAllPermissions() {
Set<Permission> superAdminPermissions = RolePermissionConfig.getPermissions(Role.SUPER_ADMIN);
Set<Permission> superAdminPermissions = rolePermissionConfig.getPermissions(Role.SUPER_ADMIN);
// SUPER_ADMIN devrait avoir accès à tout
assertTrue(superAdminPermissions.contains(Permission.SUPER_ADMIN_ACCESS), "SUPER_ADMIN devrait avoir accès super admin");
@@ -86,7 +89,7 @@ class SecurityServiceTest {
@Test
@DisplayName("Role MODERATOR devrait avoir les permissions de modération")
void testModeratorRoleHasModerationPermissions() {
Set<Permission> modPermissions = RolePermissionConfig.getPermissions(Role.MODERATOR);
Set<Permission> modPermissions = rolePermissionConfig.getPermissions(Role.MODERATOR);
assertTrue(modPermissions.contains(Permission.MODERATION_VIEW_REPORTS), "MODERATOR devrait voir les signalements");
assertTrue(modPermissions.contains(Permission.MODERATION_HANDLE_REPORTS), "MODERATOR devrait traiter les signalements");
@@ -128,23 +131,28 @@ class SecurityServiceTest {
// =========================================================================
@Test
@DisplayName("Test conversion String vers Role")
@DisplayName("Test conversion String vers Role avec fromString")
void testRoleFromString() {
assertEquals(Role.USER, Role.valueOf("USER"));
assertEquals(Role.OWNER, Role.valueOf("OWNER"));
assertEquals(Role.MANAGER, Role.valueOf("MANAGER"));
assertEquals(Role.ADMIN, Role.valueOf("ADMIN"));
assertEquals(Role.SUPER_ADMIN, Role.valueOf("SUPER_ADMIN"));
assertEquals(Role.MODERATOR, Role.valueOf("MODERATOR"));
assertEquals(Role.SUPPORT, Role.valueOf("SUPPORT"));
assertEquals(Role.FINANCE, Role.valueOf("FINANCE"));
// Nouveaux noms de rôles
assertEquals(Role.USER, Role.fromString("USER"));
assertEquals(Role.ESTABLISHMENT_OWNER, Role.fromString("ESTABLISHMENT_OWNER"));
assertEquals(Role.ESTABLISHMENT_MANAGER, Role.fromString("ESTABLISHMENT_MANAGER"));
assertEquals(Role.ADMIN, Role.fromString("ADMIN"));
assertEquals(Role.SUPER_ADMIN, Role.fromString("SUPER_ADMIN"));
assertEquals(Role.MODERATOR, Role.fromString("MODERATOR"));
assertEquals(Role.SUPPORT, Role.fromString("SUPPORT"));
assertEquals(Role.FINANCE_MANAGER, Role.fromString("FINANCE_MANAGER"));
// Rétrocompatibilité avec anciens noms
assertEquals(Role.ESTABLISHMENT_OWNER, Role.fromString("OWNER"));
assertEquals(Role.ESTABLISHMENT_MANAGER, Role.fromString("MANAGER"));
}
@Test
@DisplayName("Test que chaque rôle a au moins une permission")
void testAllRolesHavePermissions() {
for (Role role : Role.values()) {
Set<Permission> permissions = RolePermissionConfig.getPermissions(role);
Set<Permission> permissions = rolePermissionConfig.getPermissions(role);
assertFalse(permissions.isEmpty(), "Le rôle " + role + " devrait avoir au moins une permission");
}
}
@@ -161,4 +169,12 @@ class SecurityServiceTest {
assertFalse(permission.getDescription().isBlank(), "La description de " + permission + " ne devrait pas être vide");
}
}
@Test
@DisplayName("Role ESTABLISHMENT_MANAGER devrait pouvoir créer des établissements")
void testManagerCanCreateEstablishments() {
Set<Permission> managerPermissions = rolePermissionConfig.getPermissions(Role.ESTABLISHMENT_MANAGER);
assertTrue(managerPermissions.contains(Permission.ESTABLISHMENTS_CREATE),
"ESTABLISHMENT_MANAGER devrait pouvoir créer un établissement");
}
}