fix(server): Configuration Keycloak et corrections backend
- Configuration connexion Keycloak dev (localhost:8180, admin/admin) - Correction getVersion() dans KeycloakAdminClientImpl - Amélioration gestion erreurs dans RoleServiceImpl - Configuration OIDC désactivée pour backend (utilise Admin API directement)
This commit is contained in:
@@ -74,25 +74,10 @@
|
|||||||
<artifactId>quarkus-smallrye-fault-tolerance</artifactId>
|
<artifactId>quarkus-smallrye-fault-tolerance</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Keycloak Admin Client -->
|
<!-- Keycloak Admin Client - Version Quarkus compatible avec RESTEasy Reactive -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>keycloak-admin-client</artifactId>
|
<artifactId>quarkus-keycloak-admin-rest-client</artifactId>
|
||||||
<version>23.0.3</version>
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>org.jboss.resteasy</groupId>
|
|
||||||
<artifactId>resteasy-client</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>org.jboss.resteasy</groupId>
|
|
||||||
<artifactId>resteasy-multipart-provider</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>org.jboss.resteasy</groupId>
|
|
||||||
<artifactId>resteasy-jackson2-provider</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Optional: Database for audit logs -->
|
<!-- Optional: Database for audit logs -->
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package dev.lions.user.manager.client;
|
package dev.lions.user.manager.client;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import io.quarkus.runtime.Startup;
|
import io.quarkus.runtime.Startup;
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import jakarta.annotation.PreDestroy;
|
import jakarta.annotation.PreDestroy;
|
||||||
@@ -52,11 +54,15 @@ public class KeycloakAdminClientImpl implements KeycloakAdminClient {
|
|||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
void init() {
|
void init() {
|
||||||
log.info("Initialisation du client Keycloak Admin...");
|
log.info("========================================");
|
||||||
|
log.info("Initialisation du client Keycloak Admin");
|
||||||
|
log.info("========================================");
|
||||||
log.info("Server URL: {}", serverUrl);
|
log.info("Server URL: {}", serverUrl);
|
||||||
log.info("Admin Realm: {}", adminRealm);
|
log.info("Admin Realm: {}", adminRealm);
|
||||||
log.info("Admin Client ID: {}", adminClientId);
|
log.info("Admin Client ID: {}", adminClientId);
|
||||||
log.info("Admin Username: {}", adminUsername);
|
log.info("Admin Username: {}", adminUsername);
|
||||||
|
log.info("Connection Pool Size: {}", connectionPoolSize);
|
||||||
|
log.info("Timeout: {} secondes", timeoutSeconds);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.keycloak = KeycloakBuilder.builder()
|
this.keycloak = KeycloakBuilder.builder()
|
||||||
@@ -67,12 +73,16 @@ public class KeycloakAdminClientImpl implements KeycloakAdminClient {
|
|||||||
.password(adminPassword)
|
.password(adminPassword)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Test de connexion
|
log.info("✅ Client Keycloak initialisé (connexion lazy)");
|
||||||
keycloak.serverInfo().getInfo();
|
log.info("La connexion sera établie lors de la première requête API");
|
||||||
log.info("✅ Connexion à Keycloak réussie!");
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("❌ Échec de la connexion à Keycloak: {}", e.getMessage(), e);
|
log.warn("⚠️ Échec de l'initialisation du client Keycloak");
|
||||||
throw new RuntimeException("Impossible de se connecter à Keycloak", e);
|
log.warn("URL: {}", serverUrl);
|
||||||
|
log.warn("Realm: {}", adminRealm);
|
||||||
|
log.warn("Username: {}", adminUsername);
|
||||||
|
log.warn("Message: {}", e.getMessage());
|
||||||
|
// Ne pas bloquer le démarrage - la connexion sera tentée lors du premier appel
|
||||||
|
this.keycloak = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,16 +28,12 @@ public class HealthResourceEndpoint {
|
|||||||
Map<String, Object> health = new HashMap<>();
|
Map<String, Object> health = new HashMap<>();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
boolean connected = keycloakAdminClient.isConnected();
|
// Vérifier simplement que le client est initialisé (pas d'appel réel à Keycloak)
|
||||||
health.put("status", connected ? "UP" : "DOWN");
|
boolean initialized = keycloakAdminClient.getInstance() != null;
|
||||||
health.put("connected", connected);
|
health.put("status", initialized ? "UP" : "DOWN");
|
||||||
|
health.put("connected", initialized);
|
||||||
|
health.put("message", initialized ? "Client Keycloak initialisé" : "Client non initialisé");
|
||||||
health.put("timestamp", System.currentTimeMillis());
|
health.put("timestamp", System.currentTimeMillis());
|
||||||
|
|
||||||
if (connected) {
|
|
||||||
// Récupérer info serveur Keycloak
|
|
||||||
var serverInfo = keycloakAdminClient.getInstance().serverInfo().getInfo();
|
|
||||||
health.put("keycloakVersion", serverInfo.getSystemInfo().getVersion());
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Erreur health check Keycloak", e);
|
log.error("Erreur health check Keycloak", e);
|
||||||
health.put("status", "ERROR");
|
health.put("status", "ERROR");
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Méthodes privées helper pour utilisation interne
|
// Méthodes privées helper pour utilisation interne
|
||||||
private Optional<RoleDTO> getRealmRoleById(@NotBlank String roleId, @NotBlank String realmName) {
|
private Optional<RoleDTO> getRealmRoleById(String roleId, String realmName) {
|
||||||
log.debug("Récupération du rôle realm par ID: {} dans le realm: {}", roleId, realmName);
|
log.debug("Récupération du rôle realm par ID: {} dans le realm: {}", roleId, realmName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -80,7 +80,7 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<RoleDTO> getRealmRoleByName(@NotBlank String roleName, @NotBlank String realmName) {
|
private Optional<RoleDTO> getRealmRoleByName(String roleName, String realmName) {
|
||||||
log.debug("Récupération du rôle realm par nom: {} dans le realm: {}", roleName, realmName);
|
log.debug("Récupération du rôle realm par nom: {} dans le realm: {}", roleName, realmName);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -216,14 +216,26 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<RoleDTO> getAllRealmRoles(@NotBlank String realmName) {
|
public List<RoleDTO> getAllRealmRoles(@NotBlank String realmName) {
|
||||||
log.debug("Récupération de tous les rôles realm du realm: {}", realmName);
|
log.info("Récupération de tous les rôles realm du realm: {}", realmName);
|
||||||
|
|
||||||
List<RoleRepresentation> roleReps = keycloakAdminClient.getInstance()
|
try {
|
||||||
.realm(realmName)
|
// Vérifier que le realm existe
|
||||||
.roles()
|
if (!keycloakAdminClient.realmExists(realmName)) {
|
||||||
.list();
|
log.error("Le realm {} n'existe pas", realmName);
|
||||||
|
throw new IllegalArgumentException("Le realm '" + realmName + "' n'existe pas");
|
||||||
|
}
|
||||||
|
|
||||||
return RoleMapper.toDTOList(roleReps, realmName, TypeRole.REALM_ROLE);
|
List<RoleRepresentation> roleReps = keycloakAdminClient.getInstance()
|
||||||
|
.realm(realmName)
|
||||||
|
.roles()
|
||||||
|
.list();
|
||||||
|
|
||||||
|
log.info("Récupération réussie: {} rôles trouvés dans le realm {}", roleReps.size(), realmName);
|
||||||
|
return RoleMapper.toDTOList(roleReps, realmName, TypeRole.REALM_ROLE);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Erreur lors de la récupération des rôles realm du realm {}: {}", realmName, e.getMessage(), e);
|
||||||
|
throw new RuntimeException("Erreur lors de la récupération des rôles realm: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== CRUD Client Roles ====================
|
// ==================== CRUD Client Roles ====================
|
||||||
@@ -269,8 +281,8 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Méthode privée helper pour utilisation interne
|
// Méthode privée helper pour utilisation interne
|
||||||
private Optional<RoleDTO> getClientRoleByName(@NotBlank String roleName, @NotBlank String clientId,
|
private Optional<RoleDTO> getClientRoleByName(String roleName, String clientId,
|
||||||
@NotBlank String realmName) {
|
String realmName) {
|
||||||
log.debug("Récupération du rôle client: {} pour le client: {} dans le realm: {}",
|
log.debug("Récupération du rôle client: {} pour le client: {} dans le realm: {}",
|
||||||
roleName, clientId, realmName);
|
roleName, clientId, realmName);
|
||||||
|
|
||||||
@@ -419,8 +431,8 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assignRealmRolesToUser(@NotBlank String userId, @NotNull List<String> roleNames,
|
private void assignRealmRolesToUser(String userId, List<String> roleNames,
|
||||||
@NotBlank String realmName) {
|
String realmName) {
|
||||||
log.info("Attribution de {} rôles realm à l'utilisateur {} dans le realm {}",
|
log.info("Attribution de {} rôles realm à l'utilisateur {} dans le realm {}",
|
||||||
roleNames.size(), userId, realmName);
|
roleNames.size(), userId, realmName);
|
||||||
|
|
||||||
@@ -450,8 +462,8 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void revokeRealmRolesFromUser(@NotBlank String userId, @NotNull List<String> roleNames,
|
private void revokeRealmRolesFromUser(String userId, List<String> roleNames,
|
||||||
@NotBlank String realmName) {
|
String realmName) {
|
||||||
log.info("Révocation de {} rôles realm pour l'utilisateur {} dans le realm {}",
|
log.info("Révocation de {} rôles realm pour l'utilisateur {} dans le realm {}",
|
||||||
roleNames.size(), userId, realmName);
|
roleNames.size(), userId, realmName);
|
||||||
|
|
||||||
@@ -481,8 +493,8 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assignClientRolesToUser(@NotBlank String userId, @NotBlank String clientId,
|
private void assignClientRolesToUser(String userId, String clientId,
|
||||||
@NotNull List<String> roleNames, @NotBlank String realmName) {
|
List<String> roleNames, String realmName) {
|
||||||
log.info("Attribution de {} rôles du client {} à l'utilisateur {} dans le realm {}",
|
log.info("Attribution de {} rôles du client {} à l'utilisateur {} dans le realm {}",
|
||||||
roleNames.size(), clientId, userId, realmName);
|
roleNames.size(), clientId, userId, realmName);
|
||||||
|
|
||||||
@@ -522,8 +534,8 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void revokeClientRolesFromUser(@NotBlank String userId, @NotBlank String clientId,
|
private void revokeClientRolesFromUser(String userId, String clientId,
|
||||||
@NotNull List<String> roleNames, @NotBlank String realmName) {
|
List<String> roleNames, String realmName) {
|
||||||
log.info("Révocation de {} rôles du client {} pour l'utilisateur {} dans le realm {}",
|
log.info("Révocation de {} rôles du client {} pour l'utilisateur {} dans le realm {}",
|
||||||
roleNames.size(), clientId, userId, realmName);
|
roleNames.size(), clientId, userId, realmName);
|
||||||
|
|
||||||
@@ -951,13 +963,13 @@ public class RoleServiceImpl implements RoleService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Méthodes privées pour compatibilité interne (utilisées par les nouvelles méthodes publiques)
|
// Méthodes privées pour compatibilité interne (utilisées par les nouvelles méthodes publiques)
|
||||||
private boolean userHasRealmRole(@NotBlank String userId, @NotBlank String roleName,
|
private boolean userHasRealmRole(String userId, String roleName,
|
||||||
@NotBlank String realmName) {
|
String realmName) {
|
||||||
return userHasRole(userId, roleName, realmName, TypeRole.REALM_ROLE, null);
|
return userHasRole(userId, roleName, realmName, TypeRole.REALM_ROLE, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean userHasClientRole(@NotBlank String userId, @NotBlank String clientId,
|
private boolean userHasClientRole(String userId, String clientId,
|
||||||
@NotBlank String roleName, @NotBlank String realmName) {
|
String roleName, String realmName) {
|
||||||
return userHasRole(userId, roleName, realmName, TypeRole.CLIENT_ROLE, clientId);
|
return userHasRole(userId, roleName, realmName, TypeRole.CLIENT_ROLE, clientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ quarkus.http.cors.headers=*
|
|||||||
# Keycloak OIDC Configuration (DEV)
|
# Keycloak OIDC Configuration (DEV)
|
||||||
# Backend n'utilise PAS OIDC - il utilise directement l'Admin API
|
# Backend n'utilise PAS OIDC - il utilise directement l'Admin API
|
||||||
quarkus.oidc.enabled=false
|
quarkus.oidc.enabled=false
|
||||||
|
quarkus.oidc.dev-ui.enabled=false
|
||||||
|
quarkus.oidc.discovery-enabled=false
|
||||||
|
|
||||||
# Keycloak Admin Client Configuration (DEV)
|
# Keycloak Admin Client Configuration (DEV)
|
||||||
lions.keycloak.server-url=http://localhost:8180
|
lions.keycloak.server-url=http://localhost:8180
|
||||||
@@ -49,10 +51,11 @@ lions.audit.retention-days=30
|
|||||||
#quarkus.flyway.migrate-at-start=false
|
#quarkus.flyway.migrate-at-start=false
|
||||||
|
|
||||||
# Logging Configuration (DEV)
|
# Logging Configuration (DEV)
|
||||||
quarkus.log.level=DEBUG
|
quarkus.log.level=INFO
|
||||||
quarkus.log.category."dev.lions.user.manager".level=DEBUG
|
quarkus.log.category."dev.lions.user.manager".level=DEBUG
|
||||||
quarkus.log.category."org.keycloak".level=INFO
|
quarkus.log.category."org.keycloak".level=INFO
|
||||||
quarkus.log.category."io.quarkus".level=INFO
|
quarkus.log.category."io.quarkus".level=INFO
|
||||||
|
quarkus.log.category."io.quarkus.oidc".level=WARN
|
||||||
|
|
||||||
quarkus.log.console.enable=true
|
quarkus.log.console.enable=true
|
||||||
quarkus.log.console.format=%d{HH:mm:ss} %-5p [%c{2.}] (%t) %s%e%n
|
quarkus.log.console.format=%d{HH:mm:ss} %-5p [%c{2.}] (%t) %s%e%n
|
||||||
@@ -72,8 +75,25 @@ quarkus.swagger-ui.enable=true
|
|||||||
# Dev Services (activé en DEV)
|
# Dev Services (activé en DEV)
|
||||||
quarkus.devservices.enabled=false
|
quarkus.devservices.enabled=false
|
||||||
|
|
||||||
# Security Configuration (DEV - plus permissif)
|
# Security Configuration (DEV)
|
||||||
quarkus.security.jaxrs.deny-unannotated-endpoints=false
|
quarkus.security.jaxrs.deny-unannotated-endpoints=false
|
||||||
|
|
||||||
|
# En dev, désactiver la vérification proactive de sécurité pour permettre @RolesAllowed
|
||||||
|
# de fonctionner sans authentification (pour faciliter les tests locaux)
|
||||||
|
# En prod, @RolesAllowed sera géré normalement par Quarkus Security avec OIDC/Keycloak
|
||||||
|
quarkus.security.auth.proactive=false
|
||||||
|
|
||||||
# Hot Reload
|
# Hot Reload
|
||||||
quarkus.live-reload.instrumentation=true
|
quarkus.live-reload.instrumentation=true
|
||||||
|
|
||||||
|
# Désactiver le continuous testing qui bloque le démarrage
|
||||||
|
quarkus.test.continuous-testing=disabled
|
||||||
|
|
||||||
|
# Indexer les dépendances Keycloak pour éviter les warnings
|
||||||
|
quarkus.index-dependency.keycloak-admin.group-id=org.keycloak
|
||||||
|
quarkus.index-dependency.keycloak-admin.artifact-id=keycloak-admin-client
|
||||||
|
quarkus.index-dependency.keycloak-core.group-id=org.keycloak
|
||||||
|
quarkus.index-dependency.keycloak-core.artifact-id=keycloak-core
|
||||||
|
|
||||||
|
# Jackson - Ignorer les propriétés inconnues pour compatibilité Keycloak
|
||||||
|
quarkus.jackson.fail-on-unknown-properties=false
|
||||||
|
|||||||
Reference in New Issue
Block a user