Compare commits
10 Commits
c89377d12f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45ae712c0f | ||
|
|
788cc04a0b | ||
|
|
659c059c18 | ||
|
|
72da33ab5a | ||
|
|
15ec24a47c | ||
|
|
ffc420d819 | ||
|
|
0bb0198f53 | ||
|
|
e05eae08fe | ||
|
|
65d1e6a440 | ||
|
|
92d8dbc9d5 |
133
.gitignore
vendored
Normal file
133
.gitignore
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
# ============================================================================
|
||||
# Lions User Manager - Server Implementation Quarkus - .gitignore
|
||||
# ============================================================================
|
||||
|
||||
# Maven
|
||||
target/
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
.mvn/timing.properties
|
||||
.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
# Build artifacts
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
*.class
|
||||
*.idx
|
||||
|
||||
# Eclipse
|
||||
.project
|
||||
.classpath
|
||||
.settings/
|
||||
.metadata/
|
||||
bin/
|
||||
|
||||
# IntelliJ IDEA
|
||||
.idea/
|
||||
*.iml
|
||||
*.iws
|
||||
*.ipr
|
||||
out/
|
||||
|
||||
# NetBeans
|
||||
nbproject/
|
||||
nbbuild/
|
||||
nbdist/
|
||||
.nb-gradle/
|
||||
|
||||
# VS Code
|
||||
.vscode/
|
||||
*.code-workspace
|
||||
|
||||
# Mac
|
||||
.DS_Store
|
||||
|
||||
# Windows
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
|
||||
# Logs
|
||||
logs/
|
||||
*.log
|
||||
*.log.*
|
||||
hs_err_pid*.log
|
||||
|
||||
# Quarkus
|
||||
.quarkus/
|
||||
quarkus-app/
|
||||
quarkus-run.jar
|
||||
quarkus-*.dat
|
||||
|
||||
# Temporary files
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~
|
||||
*.orig
|
||||
|
||||
# Test files and reports
|
||||
test_output*.txt
|
||||
surefire-reports/
|
||||
failsafe-reports/
|
||||
*.dump
|
||||
*.dumpstream
|
||||
|
||||
# Test coverage
|
||||
.jacoco/
|
||||
jacoco.exec
|
||||
coverage/
|
||||
target/site/jacoco/
|
||||
|
||||
# Application specific
|
||||
application-local.properties
|
||||
application-*.local.properties
|
||||
|
||||
# Configuration files with sensitive data
|
||||
*.local.json
|
||||
|
||||
# Token and authentication files
|
||||
token.json
|
||||
token.txt
|
||||
*.token
|
||||
|
||||
# Generated sources
|
||||
generated-sources/
|
||||
generated-test-sources/
|
||||
|
||||
# Maven status
|
||||
maven-status/
|
||||
|
||||
# Build metrics
|
||||
build-metrics.json
|
||||
|
||||
# Quarkus Dev Services
|
||||
.devservices/
|
||||
|
||||
# Fichiers META-INF générés (reflection-config.json est généré par Quarkus)
|
||||
**/META-INF/reflection-config.json
|
||||
|
||||
# IDE specific
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
|
||||
# OS specific
|
||||
.DS_Store?
|
||||
._*
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
|
||||
# Lombok configuration (généré automatiquement)
|
||||
lombok.config
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
86
Dockerfile.prod
Normal file
86
Dockerfile.prod
Normal file
@@ -0,0 +1,86 @@
|
||||
####
|
||||
# Dockerfile de production pour Lions User Manager Server (Backend)
|
||||
# Multi-stage build optimisé avec sécurité renforcée
|
||||
# Basé sur la structure de btpxpress-server
|
||||
####
|
||||
|
||||
## Stage 1 : Build avec Maven
|
||||
FROM maven:3.9.6-eclipse-temurin-17 AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copier pom.xml et télécharger les dépendances (cache Docker)
|
||||
COPY pom.xml .
|
||||
RUN mvn dependency:go-offline -B
|
||||
|
||||
# Copier le code source
|
||||
COPY src ./src
|
||||
|
||||
# Construire l'application avec profil production
|
||||
RUN mvn clean package -DskipTests -B -Dquarkus.profile=prod
|
||||
|
||||
## Stage 2 : Image de production optimisée
|
||||
FROM registry.access.redhat.com/ubi8/openjdk-17:1.18
|
||||
|
||||
ENV LANGUAGE='en_US:en'
|
||||
|
||||
# Configuration des variables d'environnement pour production
|
||||
ENV QUARKUS_PROFILE=prod
|
||||
ENV DB_URL=jdbc:postgresql://postgresql:5432/lions_audit
|
||||
ENV DB_USERNAME=lions_audit_user
|
||||
ENV DB_PASSWORD=changeme
|
||||
ENV SERVER_PORT=8080
|
||||
|
||||
# Configuration Keycloak/OIDC (production)
|
||||
ENV QUARKUS_OIDC_AUTH_SERVER_URL=https://security.lions.dev/realms/master
|
||||
ENV QUARKUS_OIDC_CLIENT_ID=lions-user-manager
|
||||
ENV KEYCLOAK_CLIENT_SECRET=changeme
|
||||
ENV QUARKUS_OIDC_TLS_VERIFICATION=required
|
||||
|
||||
# Configuration Keycloak Admin Client
|
||||
ENV LIONS_KEYCLOAK_SERVER_URL=https://security.lions.dev
|
||||
ENV LIONS_KEYCLOAK_ADMIN_REALM=master
|
||||
ENV LIONS_KEYCLOAK_ADMIN_CLIENT_ID=admin-cli
|
||||
ENV LIONS_KEYCLOAK_ADMIN_USERNAME=admin
|
||||
ENV LIONS_KEYCLOAK_ADMIN_PASSWORD=changeme
|
||||
|
||||
# Configuration CORS pour production
|
||||
ENV QUARKUS_HTTP_CORS_ORIGINS=https://user-manager.lions.dev,https://admin.lions.dev
|
||||
ENV QUARKUS_HTTP_CORS_ALLOW_CREDENTIALS=true
|
||||
|
||||
# Installer curl pour les health checks
|
||||
USER root
|
||||
RUN microdnf install curl -y && microdnf clean all
|
||||
RUN mkdir -p /app/logs && chown -R 185:185 /app/logs
|
||||
USER 185
|
||||
|
||||
# Copier l'application depuis le builder
|
||||
COPY --from=builder --chown=185 /app/target/quarkus-app/lib/ /deployments/lib/
|
||||
COPY --from=builder --chown=185 /app/target/quarkus-app/*.jar /deployments/
|
||||
COPY --from=builder --chown=185 /app/target/quarkus-app/app/ /deployments/app/
|
||||
COPY --from=builder --chown=185 /app/target/quarkus-app/quarkus/ /deployments/quarkus/
|
||||
|
||||
# Exposer le port
|
||||
EXPOSE 8080
|
||||
|
||||
# Variables JVM optimisées pour production avec sécurité
|
||||
ENV JAVA_OPTS="-Xmx1g -Xms512m \
|
||||
-XX:+UseG1GC \
|
||||
-XX:MaxGCPauseMillis=200 \
|
||||
-XX:+UseStringDeduplication \
|
||||
-XX:+ParallelRefProcEnabled \
|
||||
-XX:+HeapDumpOnOutOfMemoryError \
|
||||
-XX:HeapDumpPath=/app/logs/heapdump.hprof \
|
||||
-Djava.security.egd=file:/dev/./urandom \
|
||||
-Djava.awt.headless=true \
|
||||
-Dfile.encoding=UTF-8 \
|
||||
-Djava.util.logging.manager=org.jboss.logmanager.LogManager \
|
||||
-Dquarkus.profile=${QUARKUS_PROFILE}"
|
||||
|
||||
# Point d'entrée avec profil production
|
||||
ENTRYPOINT ["sh", "-c", "exec java $JAVA_OPTS -jar /deployments/quarkus-run.jar"]
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
|
||||
CMD curl -f http://localhost:8080/q/health/ready || exit 1
|
||||
|
||||
85
logs/dev/lions-user-manager.log
Normal file
85
logs/dev/lions-user-manager.log
Normal file
@@ -0,0 +1,85 @@
|
||||
2025-12-06 19:32:12,544 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] WARN [io.qua.config] (Quarkus Main Thread) Unrecognized configuration key "quarkus.smallrye-fault-tolerance.enabled" was provided; it will be ignored; verify that the dependency extension for this configuration is set or that you did not make a typo
|
||||
2025-12-06 19:32:12,545 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] WARN [io.qua.config] (Quarkus Main Thread) Unrecognized configuration key "quarkus.security.auth.enabled" was provided; it will be ignored; verify that the dependency extension for this configuration is set or that you did not make a typo
|
||||
2025-12-06 19:32:12,545 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] WARN [io.qua.config] (Quarkus Main Thread) Unrecognized configuration key "quarkus.security.auth.proactive" was provided; it will be ignored; verify that the dependency extension for this configuration is set or that you did not make a typo
|
||||
2025-12-06 19:32:12,545 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] WARN [io.qua.config] (Quarkus Main Thread) Unrecognized configuration key "quarkus.oidc.verify-access-token" was provided; it will be ignored; verify that the dependency extension for this configuration is set or that you did not make a typo
|
||||
2025-12-06 19:32:15,688 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.con.KeycloakTestUserConfig] (Quarkus Main Thread) Configuration automatique de Keycloak DÉSACTIVÉE
|
||||
2025-12-06 19:32:15,697 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.con.KeycloakTestUserConfig] (Quarkus Main Thread) Utiliser le script create-roles-and-assign.sh pour configurer Keycloak manuellement
|
||||
2025-12-06 19:32:15,717 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) ========================================
|
||||
2025-12-06 19:32:15,718 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Initialisation du client Keycloak Admin
|
||||
2025-12-06 19:32:15,719 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) ========================================
|
||||
2025-12-06 19:32:15,720 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Server URL: http://localhost:8180
|
||||
2025-12-06 19:32:15,720 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Admin Realm: master
|
||||
2025-12-06 19:32:15,721 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Admin Client ID: admin-cli
|
||||
2025-12-06 19:32:15,722 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Admin Username: admin
|
||||
2025-12-06 19:32:15,723 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Connection Pool Size: 5
|
||||
2025-12-06 19:32:15,725 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Timeout: 30 secondes
|
||||
2025-12-06 19:32:15,762 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) ✅ Client Keycloak initialisé (connexion lazy)
|
||||
2025-12-06 19:32:15,763 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) La connexion sera établie lors de la première requête API
|
||||
2025-12-06 19:32:15,850 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [io.quarkus] (Quarkus Main Thread) lions-user-manager-server 1.0.0 on JVM (powered by Quarkus 3.15.1) started in 12.905s. Listening on: http://localhost:8081
|
||||
2025-12-06 19:32:15,854 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
|
||||
2025-12-06 19:32:15,857 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [io.quarkus] (Quarkus Main Thread) Installed features: [agroal, cdi, flyway, hibernate-orm, hibernate-orm-panache, hibernate-validator, jdbc-postgresql, micrometer, narayana-jta, oidc, rest, rest-client, rest-client-jackson, rest-jackson, security, smallrye-context-propagation, smallrye-fault-tolerance, smallrye-health, smallrye-openapi, swagger-ui, vertx]
|
||||
2025-12-06 19:32:32,334 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-3) Resolved OIDC tenant id: Default
|
||||
2025-12-06 19:32:32,338 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-3) Starting a bearer access token authentication
|
||||
2025-12-06 19:32:32,346 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-3) Looking for a token in the authorization header
|
||||
2025-12-06 19:32:32,347 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-3) Authorization scheme: Bearer
|
||||
2025-12-06 19:32:32,352 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-3) Starting creating SecurityIdentity
|
||||
2025-12-06 19:32:32,360 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-3) Verifying the JWT token with the local JWK keys
|
||||
2025-12-06 19:32:32,516 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-1) Mode dev détecté (profile=dev, oidc.enabled=true): remplacement du SecurityContext pour le chemin /api/users/search
|
||||
2025-12-06 19:32:32,517 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-1) SecurityContext remplacé - isUserInRole('admin')=true, isUserInRole('user_manager')=true
|
||||
2025-12-06 19:32:32,583 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.res.UserResource] (executor-thread-1) POST /api/users/search - Recherche d'utilisateurs
|
||||
2025-12-06 19:32:32,601 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.ser.imp.UserServiceImpl] (executor-thread-1) Recherche d'utilisateurs avec critères: UserSearchCriteriaDTO(searchTerm=null, username=null, email=null, prenom=null, nom=null, telephone=null, statut=null, enabled=null, emailVerified=null, realmRoles=null, clientRoles=null, groups=null, clientName=null, organisation=null, departement=null, fonction=null, pays=null, ville=null, dateCreationMin=null, dateCreationMax=null, derniereConnexionMin=null, derniereConnexionMax=null, hasRequiredActions=null, isLocked=null, isExpired=null, hasActiveSessions=null, realmName=lions-user-manager, page=0, pageSize=20, maxResults=null, sortBy=username, sortOrder=ASC, includeRoles=false, includeGroups=false, includeAttributes=false, includeSessionInfo=false)
|
||||
2025-12-06 19:37:20,766 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-4) Resolved OIDC tenant id: Default
|
||||
2025-12-06 19:37:20,767 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-4) Starting a bearer access token authentication
|
||||
2025-12-06 19:37:20,768 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-4) Looking for a token in the authorization header
|
||||
2025-12-06 19:37:20,769 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-4) Authorization scheme: Bearer
|
||||
2025-12-06 19:37:20,769 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-4) Starting creating SecurityIdentity
|
||||
2025-12-06 19:37:20,770 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-4) Verifying the JWT token with the local JWK keys
|
||||
2025-12-06 19:37:20,778 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-1) Mode dev détecté (profile=dev, oidc.enabled=true): remplacement du SecurityContext pour le chemin /api/users/search
|
||||
2025-12-06 19:37:20,778 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-1) SecurityContext remplacé - isUserInRole('admin')=true, isUserInRole('user_manager')=true
|
||||
2025-12-06 19:37:20,780 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.res.UserResource] (executor-thread-1) POST /api/users/search - Recherche d'utilisateurs
|
||||
2025-12-06 19:37:20,781 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.ser.imp.UserServiceImpl] (executor-thread-1) Recherche d'utilisateurs avec critères: UserSearchCriteriaDTO(searchTerm=null, username=null, email=null, prenom=null, nom=null, telephone=null, statut=null, enabled=null, emailVerified=null, realmRoles=null, clientRoles=null, groups=null, clientName=null, organisation=null, departement=null, fonction=null, pays=null, ville=null, dateCreationMin=null, dateCreationMax=null, derniereConnexionMin=null, derniereConnexionMax=null, hasRequiredActions=null, isLocked=null, isExpired=null, hasActiveSessions=null, realmName=lions-user-manager, page=0, pageSize=20, maxResults=null, sortBy=username, sortOrder=ASC, includeRoles=false, includeGroups=false, includeAttributes=false, includeSessionInfo=false)
|
||||
2025-12-06 19:38:43,981 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-3) Resolved OIDC tenant id: Default
|
||||
2025-12-06 19:38:43,982 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-3) Starting a bearer access token authentication
|
||||
2025-12-06 19:38:43,984 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-3) Looking for a token in the authorization header
|
||||
2025-12-06 19:38:43,985 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-3) Authorization scheme: Bearer
|
||||
2025-12-06 19:38:43,986 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-3) Starting creating SecurityIdentity
|
||||
2025-12-06 19:38:43,988 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-3) Verifying the JWT token with the local JWK keys
|
||||
2025-12-06 19:38:44,003 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-1) Mode dev détecté (profile=dev, oidc.enabled=true): remplacement du SecurityContext pour le chemin /api/users/search
|
||||
2025-12-06 19:38:44,004 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-1) SecurityContext remplacé - isUserInRole('admin')=true, isUserInRole('user_manager')=true
|
||||
2025-12-06 19:38:44,009 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.res.UserResource] (executor-thread-1) POST /api/users/search - Recherche d'utilisateurs
|
||||
2025-12-06 19:38:44,011 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.ser.imp.UserServiceImpl] (executor-thread-1) Recherche d'utilisateurs avec critères: UserSearchCriteriaDTO(searchTerm=null, username=null, email=null, prenom=null, nom=null, telephone=null, statut=null, enabled=null, emailVerified=null, realmRoles=null, clientRoles=null, groups=null, clientName=null, organisation=null, departement=null, fonction=null, pays=null, ville=null, dateCreationMin=null, dateCreationMax=null, derniereConnexionMin=null, derniereConnexionMax=null, hasRequiredActions=null, isLocked=null, isExpired=null, hasActiveSessions=null, realmName=lions-user-manager, page=0, pageSize=20, maxResults=null, sortBy=username, sortOrder=ASC, includeRoles=false, includeGroups=false, includeAttributes=false, includeSessionInfo=false)
|
||||
2025-12-06 19:44:46,850 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-4) Resolved OIDC tenant id: Default
|
||||
2025-12-06 19:44:46,851 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-4) Starting a bearer access token authentication
|
||||
2025-12-06 19:44:46,852 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-4) Looking for a token in the authorization header
|
||||
2025-12-06 19:44:46,853 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-4) Authorization scheme: Bearer
|
||||
2025-12-06 19:44:46,853 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-4) Starting creating SecurityIdentity
|
||||
2025-12-06 19:44:46,854 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-4) Verifying the JWT token with the local JWK keys
|
||||
2025-12-06 19:44:46,862 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-1) Mode dev détecté (profile=dev, oidc.enabled=true): remplacement du SecurityContext pour le chemin /api/users/search
|
||||
2025-12-06 19:44:46,863 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-1) SecurityContext remplacé - isUserInRole('admin')=true, isUserInRole('user_manager')=true
|
||||
2025-12-06 19:44:46,865 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.res.UserResource] (executor-thread-1) POST /api/users/search - Recherche d'utilisateurs
|
||||
2025-12-06 19:44:46,865 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.ser.imp.UserServiceImpl] (executor-thread-1) Recherche d'utilisateurs avec critères: UserSearchCriteriaDTO(searchTerm=null, username=null, email=null, prenom=null, nom=null, telephone=null, statut=null, enabled=null, emailVerified=null, realmRoles=null, clientRoles=null, groups=null, clientName=null, organisation=null, departement=null, fonction=null, pays=null, ville=null, dateCreationMin=null, dateCreationMax=null, derniereConnexionMin=null, derniereConnexionMax=null, hasRequiredActions=null, isLocked=null, isExpired=null, hasActiveSessions=null, realmName=lions-user-manager, page=0, pageSize=20, maxResults=null, sortBy=username, sortOrder=ASC, includeRoles=false, includeGroups=false, includeAttributes=false, includeSessionInfo=false)
|
||||
2025-12-06 19:54:25,525 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-3) Resolved OIDC tenant id: Default
|
||||
2025-12-06 19:54:25,526 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-3) Starting a bearer access token authentication
|
||||
2025-12-06 19:54:25,527 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-3) Looking for a token in the authorization header
|
||||
2025-12-06 19:54:25,527 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-3) Bearer access token is not available
|
||||
2025-12-06 21:36:12,969 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-4) Resolved OIDC tenant id: Default
|
||||
2025-12-06 21:36:12,975 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-4) Starting a bearer access token authentication
|
||||
2025-12-06 21:36:12,977 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-4) Looking for a token in the authorization header
|
||||
2025-12-06 21:36:12,977 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-4) Authorization scheme: Bearer
|
||||
2025-12-06 21:36:12,981 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-4) Starting creating SecurityIdentity
|
||||
2025-12-06 21:36:12,985 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-4) Verifying the JWT token with the local JWK keys
|
||||
2025-12-06 21:36:13,006 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-1) Mode dev détecté (profile=dev, oidc.enabled=true): remplacement du SecurityContext pour le chemin /api/users/search
|
||||
2025-12-06 21:36:13,009 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-1) SecurityContext remplacé - isUserInRole('admin')=true, isUserInRole('user_manager')=true
|
||||
2025-12-06 21:36:13,015 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.res.UserResource] (executor-thread-1) POST /api/users/search - Recherche d'utilisateurs
|
||||
2025-12-06 21:36:13,016 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.ser.imp.UserServiceImpl] (executor-thread-1) Recherche d'utilisateurs avec critères: UserSearchCriteriaDTO(searchTerm=null, username=null, email=null, prenom=null, nom=null, telephone=null, statut=null, enabled=null, emailVerified=null, realmRoles=null, clientRoles=null, groups=null, clientName=null, organisation=null, departement=null, fonction=null, pays=null, ville=null, dateCreationMin=null, dateCreationMax=null, derniereConnexionMin=null, derniereConnexionMax=null, hasRequiredActions=null, isLocked=null, isExpired=null, hasActiveSessions=null, realmName=lions-user-manager, page=0, pageSize=20, maxResults=null, sortBy=username, sortOrder=ASC, includeRoles=false, includeGroups=false, includeAttributes=false, includeSessionInfo=false)
|
||||
2025-12-06 21:36:22,446 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-3) Resolved OIDC tenant id: Default
|
||||
2025-12-06 21:36:22,447 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-3) Starting a bearer access token authentication
|
||||
2025-12-06 21:36:22,448 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-3) Looking for a token in the authorization header
|
||||
2025-12-06 21:36:22,448 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-3) Authorization scheme: Bearer
|
||||
2025-12-06 21:36:22,448 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-3) Starting creating SecurityIdentity
|
||||
2025-12-06 21:36:22,449 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-3) Verifying the JWT token with the local JWK keys
|
||||
2025-12-06 21:36:22,455 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-1) Mode dev détecté (profile=dev, oidc.enabled=true): remplacement du SecurityContext pour le chemin /api/roles/realm
|
||||
2025-12-06 21:36:22,456 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] DEBUG [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-1) SecurityContext remplacé - isUserInRole('admin')=true, isUserInRole('user_manager')=true
|
||||
2025-12-06 21:36:22,469 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.res.RoleResource] (executor-thread-1) GET /api/roles/realm - realm: lions-user-manager
|
||||
2025-12-06 21:36:22,474 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.ser.imp.RoleServiceImpl] (executor-thread-1) Récupération de tous les rôles realm du realm: lions-user-manager
|
||||
2025-12-06 21:36:23,100 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[59128] INFO [dev.lio.use.man.ser.imp.RoleServiceImpl] (executor-thread-1) Récupération réussie: 8 rôles trouvés dans le realm lions-user-manager
|
||||
22
logs/dev/lions-user-manager.log.1
Normal file
22
logs/dev/lions-user-manager.log.1
Normal file
@@ -0,0 +1,22 @@
|
||||
2025-12-06 19:31:04,801 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.con.KeycloakTestUserConfig] (Quarkus Main Thread) Configuration automatique de Keycloak DÉSACTIVÉE
|
||||
2025-12-06 19:31:04,802 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.con.KeycloakTestUserConfig] (Quarkus Main Thread) Utiliser le script create-roles-and-assign.sh pour configurer Keycloak manuellement
|
||||
2025-12-06 19:31:04,810 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) ========================================
|
||||
2025-12-06 19:31:04,811 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Initialisation du client Keycloak Admin
|
||||
2025-12-06 19:31:04,811 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) ========================================
|
||||
2025-12-06 19:31:04,813 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Server URL: http://localhost:8180
|
||||
2025-12-06 19:31:04,813 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Admin Realm: master
|
||||
2025-12-06 19:31:04,814 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Admin Client ID: admin-cli
|
||||
2025-12-06 19:31:04,816 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Admin Username: admin
|
||||
2025-12-06 19:31:04,817 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Connection Pool Size: 5
|
||||
2025-12-06 19:31:04,819 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Timeout: 30 secondes
|
||||
2025-12-06 19:31:04,825 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) ✅ Client Keycloak initialisé (connexion lazy)
|
||||
2025-12-06 19:31:04,826 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) La connexion sera établie lors de la première requête API
|
||||
2025-12-06 19:31:04,827 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [io.quarkus] (Quarkus Main Thread) lions-user-manager-server 1.0.0 on JVM (powered by Quarkus 3.15.1) started in 4.823s. Listening on: http://localhost:8081
|
||||
2025-12-06 19:31:04,828 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
|
||||
2025-12-06 19:31:04,828 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [io.quarkus] (Quarkus Main Thread) Installed features: [agroal, cdi, flyway, hibernate-orm, hibernate-orm-panache, hibernate-validator, jdbc-postgresql, micrometer, narayana-jta, oidc, rest, rest-client, rest-client-jackson, rest-jackson, security, smallrye-context-propagation, smallrye-fault-tolerance, smallrye-health, smallrye-openapi, swagger-ui, vertx]
|
||||
2025-12-06 19:31:04,829 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [io.qua.dep.dev.RuntimeUpdatesProcessor] (Aesh InputStream Reader) Live reload total time: 5.173s
|
||||
2025-12-06 19:31:04,854 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] WARNING [org.aes.rea.ter.imp.AbstractWindowsTerminal] (Console Shutdown Hook) Failed to write out.
|
||||
2025-12-06 19:31:04,910 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] WARNING [org.aes.rea.ter.imp.AbstractWindowsTerminal] (Shutdown thread) Failed to write out.
|
||||
2025-12-06 19:31:04,910 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Shutdown thread) Fermeture de la connexion Keycloak...
|
||||
2025-12-06 19:31:04,913 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] WARNING [org.aes.rea.ter.imp.AbstractWindowsTerminal] (Shutdown thread) Failed to write out.
|
||||
2025-12-06 19:31:04,913 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [io.quarkus] (Shutdown thread) lions-user-manager-server stopped in 0.080s
|
||||
72
logs/dev/lions-user-manager.log.2
Normal file
72
logs/dev/lions-user-manager.log.2
Normal file
@@ -0,0 +1,72 @@
|
||||
2025-12-06 17:30:27,357 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] WARN [io.qua.config] (Quarkus Main Thread) Unrecognized configuration key "quarkus.smallrye-fault-tolerance.enabled" was provided; it will be ignored; verify that the dependency extension for this configuration is set or that you did not make a typo
|
||||
2025-12-06 17:30:27,358 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] WARN [io.qua.config] (Quarkus Main Thread) Unrecognized configuration key "quarkus.security.auth.enabled" was provided; it will be ignored; verify that the dependency extension for this configuration is set or that you did not make a typo
|
||||
2025-12-06 17:30:27,358 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] WARN [io.qua.config] (Quarkus Main Thread) Unrecognized configuration key "quarkus.security.auth.proactive" was provided; it will be ignored; verify that the dependency extension for this configuration is set or that you did not make a typo
|
||||
2025-12-06 17:30:27,358 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] WARN [io.qua.config] (Quarkus Main Thread) Unrecognized configuration key "quarkus.oidc.verify-access-token" was provided; it will be ignored; verify that the dependency extension for this configuration is set or that you did not make a typo
|
||||
2025-12-06 17:30:29,096 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.con.KeycloakTestUserConfig] (Quarkus Main Thread) Configuration automatique de Keycloak DÉSACTIVÉE
|
||||
2025-12-06 17:30:29,102 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.con.KeycloakTestUserConfig] (Quarkus Main Thread) Utiliser le script create-roles-and-assign.sh pour configurer Keycloak manuellement
|
||||
2025-12-06 17:30:29,112 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) ========================================
|
||||
2025-12-06 17:30:29,113 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Initialisation du client Keycloak Admin
|
||||
2025-12-06 17:30:29,113 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) ========================================
|
||||
2025-12-06 17:30:29,113 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Server URL: http://localhost:8180
|
||||
2025-12-06 17:30:29,114 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Admin Realm: master
|
||||
2025-12-06 17:30:29,114 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Admin Client ID: admin-cli
|
||||
2025-12-06 17:30:29,115 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Admin Username: admin
|
||||
2025-12-06 17:30:29,115 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Connection Pool Size: 5
|
||||
2025-12-06 17:30:29,115 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Timeout: 30 secondes
|
||||
2025-12-06 17:30:29,138 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) ✅ Client Keycloak initialisé (connexion lazy)
|
||||
2025-12-06 17:30:29,139 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) La connexion sera établie lors de la première requête API
|
||||
2025-12-06 17:30:29,175 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [io.quarkus] (Quarkus Main Thread) lions-user-manager-server 1.0.0 on JVM (powered by Quarkus 3.15.1) started in 7.308s. Listening on: http://localhost:8081
|
||||
2025-12-06 17:30:29,176 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
|
||||
2025-12-06 17:30:29,176 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [io.quarkus] (Quarkus Main Thread) Installed features: [agroal, cdi, flyway, hibernate-orm, hibernate-orm-panache, hibernate-validator, jdbc-postgresql, micrometer, narayana-jta, oidc, rest, rest-client, rest-client-jackson, rest-jackson, security, smallrye-context-propagation, smallrye-fault-tolerance, smallrye-health, smallrye-openapi, swagger-ui, vertx]
|
||||
2025-12-06 17:46:15,854 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-3) Resolved OIDC tenant id: Default
|
||||
2025-12-06 17:46:15,855 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-3) Starting a bearer access token authentication
|
||||
2025-12-06 17:46:15,855 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-3) Looking for a token in the authorization header
|
||||
2025-12-06 17:46:15,856 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-3) Bearer access token is not available
|
||||
2025-12-06 17:46:16,018 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-3) Resolved OIDC tenant id: Default
|
||||
2025-12-06 17:46:16,018 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-3) Starting a bearer access token authentication
|
||||
2025-12-06 17:46:16,019 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-3) Looking for a token in the authorization header
|
||||
2025-12-06 17:46:16,020 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-3) Bearer access token is not available
|
||||
2025-12-06 19:15:56,656 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-4) Resolved OIDC tenant id: Default
|
||||
2025-12-06 19:15:56,669 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-4) Starting a bearer access token authentication
|
||||
2025-12-06 19:15:56,671 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-4) Looking for a token in the authorization header
|
||||
2025-12-06 19:15:56,672 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-4) Authorization scheme: Bearer
|
||||
2025-12-06 19:15:56,677 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-4) Starting creating SecurityIdentity
|
||||
2025-12-06 19:15:56,705 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-4) Verifying the JWT token with the local JWK keys
|
||||
2025-12-06 19:15:57,006 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-1) Mode dev détecté (profile=dev, oidc.enabled=true): remplacement du SecurityContext pour le chemin /api/users/search
|
||||
2025-12-06 19:15:57,011 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-1) SecurityContext remplacé - isUserInRole('admin')=true, isUserInRole('user_manager')=true
|
||||
2025-12-06 19:15:57,145 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.res.UserResource] (executor-thread-1) POST /api/users/search - Recherche d'utilisateurs
|
||||
2025-12-06 19:15:57,182 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.ser.imp.UserServiceImpl] (executor-thread-1) Recherche d'utilisateurs avec critères: UserSearchCriteriaDTO(searchTerm=null, username=null, email=null, prenom=null, nom=null, telephone=null, statut=null, enabled=null, emailVerified=null, realmRoles=null, clientRoles=null, groups=null, clientName=null, organisation=null, departement=null, fonction=null, pays=null, ville=null, dateCreationMin=null, dateCreationMax=null, derniereConnexionMin=null, derniereConnexionMax=null, hasRequiredActions=null, isLocked=null, isExpired=null, hasActiveSessions=null, realmName=lions-user-manager, page=0, pageSize=20, maxResults=null, sortBy=username, sortOrder=ASC, includeRoles=false, includeGroups=false, includeAttributes=false, includeSessionInfo=false)
|
||||
2025-12-06 19:23:16,924 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-3) Resolved OIDC tenant id: Default
|
||||
2025-12-06 19:23:16,925 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-3) Starting a bearer access token authentication
|
||||
2025-12-06 19:23:16,927 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-3) Looking for a token in the authorization header
|
||||
2025-12-06 19:23:16,927 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-3) Authorization scheme: Bearer
|
||||
2025-12-06 19:23:16,928 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-3) Starting creating SecurityIdentity
|
||||
2025-12-06 19:23:16,929 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-3) Verifying the JWT token with the local JWK keys
|
||||
2025-12-06 19:23:16,941 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-1) Mode dev détecté (profile=dev, oidc.enabled=true): remplacement du SecurityContext pour le chemin /api/users/search
|
||||
2025-12-06 19:23:16,942 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-1) SecurityContext remplacé - isUserInRole('admin')=true, isUserInRole('user_manager')=true
|
||||
2025-12-06 19:23:16,944 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.res.UserResource] (executor-thread-1) POST /api/users/search - Recherche d'utilisateurs
|
||||
2025-12-06 19:23:16,945 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.ser.imp.UserServiceImpl] (executor-thread-1) Recherche d'utilisateurs avec critères: UserSearchCriteriaDTO(searchTerm=null, username=null, email=null, prenom=null, nom=null, telephone=null, statut=null, enabled=null, emailVerified=null, realmRoles=null, clientRoles=null, groups=null, clientName=null, organisation=null, departement=null, fonction=null, pays=null, ville=null, dateCreationMin=null, dateCreationMax=null, derniereConnexionMin=null, derniereConnexionMax=null, hasRequiredActions=null, isLocked=null, isExpired=null, hasActiveSessions=null, realmName=lions-user-manager, page=0, pageSize=20, maxResults=null, sortBy=username, sortOrder=ASC, includeRoles=false, includeGroups=false, includeAttributes=false, includeSessionInfo=false)
|
||||
2025-12-06 19:30:12,829 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-4) Resolved OIDC tenant id: Default
|
||||
2025-12-06 19:30:12,831 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-4) Starting a bearer access token authentication
|
||||
2025-12-06 19:30:12,831 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-4) Looking for a token in the authorization header
|
||||
2025-12-06 19:30:12,833 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-4) Authorization scheme: Bearer
|
||||
2025-12-06 19:30:12,835 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-4) Starting creating SecurityIdentity
|
||||
2025-12-06 19:30:12,837 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-4) Verifying the JWT token with the local JWK keys
|
||||
2025-12-06 19:30:12,843 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-1) Mode dev détecté (profile=dev, oidc.enabled=true): remplacement du SecurityContext pour le chemin /api/users/search
|
||||
2025-12-06 19:30:12,844 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-1) SecurityContext remplacé - isUserInRole('admin')=true, isUserInRole('user_manager')=true
|
||||
2025-12-06 19:30:12,845 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.res.UserResource] (executor-thread-1) POST /api/users/search - Recherche d'utilisateurs
|
||||
2025-12-06 19:30:12,845 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.ser.imp.UserServiceImpl] (executor-thread-1) Recherche d'utilisateurs avec critères: UserSearchCriteriaDTO(searchTerm=null, username=null, email=null, prenom=null, nom=null, telephone=null, statut=null, enabled=null, emailVerified=null, realmRoles=null, clientRoles=null, groups=null, clientName=null, organisation=null, departement=null, fonction=null, pays=null, ville=null, dateCreationMin=null, dateCreationMax=null, derniereConnexionMin=null, derniereConnexionMax=null, hasRequiredActions=null, isLocked=null, isExpired=null, hasActiveSessions=null, realmName=lions-user-manager, page=0, pageSize=20, maxResults=null, sortBy=username, sortOrder=ASC, includeRoles=false, includeGroups=false, includeAttributes=false, includeSessionInfo=false)
|
||||
2025-12-06 19:30:42,870 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-4) Resolved OIDC tenant id: Default
|
||||
2025-12-06 19:30:42,871 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-4) Starting a bearer access token authentication
|
||||
2025-12-06 19:30:42,871 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-4) Looking for a token in the authorization header
|
||||
2025-12-06 19:30:42,872 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-4) Authorization scheme: Bearer
|
||||
2025-12-06 19:30:42,872 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-4) Starting creating SecurityIdentity
|
||||
2025-12-06 19:30:42,872 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-4) Verifying the JWT token with the local JWK keys
|
||||
2025-12-06 19:30:42,881 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-1) Mode dev détecté (profile=dev, oidc.enabled=true): remplacement du SecurityContext pour le chemin /api/users/search
|
||||
2025-12-06 19:30:42,883 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] DEBUG [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-1) SecurityContext remplacé - isUserInRole('admin')=true, isUserInRole('user_manager')=true
|
||||
2025-12-06 19:30:42,885 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.res.UserResource] (executor-thread-1) POST /api/users/search - Recherche d'utilisateurs
|
||||
2025-12-06 19:30:42,886 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.ser.imp.UserServiceImpl] (executor-thread-1) Recherche d'utilisateurs avec critères: UserSearchCriteriaDTO(searchTerm=null, username=null, email=null, prenom=null, nom=null, telephone=null, statut=null, enabled=null, emailVerified=null, realmRoles=null, clientRoles=null, groups=null, clientName=null, organisation=null, departement=null, fonction=null, pays=null, ville=null, dateCreationMin=null, dateCreationMax=null, derniereConnexionMin=null, derniereConnexionMax=null, hasRequiredActions=null, isLocked=null, isExpired=null, hasActiveSessions=null, realmName=lions-user-manager, page=0, pageSize=20, maxResults=null, sortBy=username, sortOrder=ASC, includeRoles=false, includeGroups=false, includeAttributes=false, includeSessionInfo=false)
|
||||
2025-12-06 19:30:59,515 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [io.qua.dep.dev.RuntimeUpdatesProcessor] (Aesh InputStream Reader) Live reload disabled
|
||||
2025-12-06 19:30:59,681 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [io.qua.dep.dev.RuntimeUpdatesProcessor] (Aesh InputStream Reader) Restarting as requested by the user.
|
||||
2025-12-06 19:30:59,982 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Fermeture de la connexion Keycloak...
|
||||
2025-12-06 19:30:59,993 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[53764] INFO [io.quarkus] (Quarkus Main Thread) lions-user-manager-server stopped in 0.306s
|
||||
112
logs/dev/lions-user-manager.log.3
Normal file
112
logs/dev/lions-user-manager.log.3
Normal file
@@ -0,0 +1,112 @@
|
||||
2025-12-05 22:20:06,129 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.con.KeycloakTestUserConfig] (Quarkus Main Thread) Configuration automatique de Keycloak DÉSACTIVÉE
|
||||
2025-12-05 22:20:06,130 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.con.KeycloakTestUserConfig] (Quarkus Main Thread) Utiliser le script create-roles-and-assign.sh pour configurer Keycloak manuellement
|
||||
2025-12-05 22:20:06,141 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) ========================================
|
||||
2025-12-05 22:20:06,144 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Initialisation du client Keycloak Admin
|
||||
2025-12-05 22:20:06,144 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) ========================================
|
||||
2025-12-05 22:20:06,144 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Server URL: http://localhost:8180
|
||||
2025-12-05 22:20:06,146 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Admin Realm: master
|
||||
2025-12-05 22:20:06,146 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Admin Client ID: admin-cli
|
||||
2025-12-05 22:20:06,147 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Admin Username: admin
|
||||
2025-12-05 22:20:06,147 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Connection Pool Size: 5
|
||||
2025-12-05 22:20:06,147 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) Timeout: 30 secondes
|
||||
2025-12-05 22:20:06,158 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) ✅ Client Keycloak initialisé (connexion lazy)
|
||||
2025-12-05 22:20:06,158 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Quarkus Main Thread) La connexion sera établie lors de la première requête API
|
||||
2025-12-05 22:20:06,159 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [io.quarkus] (Quarkus Main Thread) lions-user-manager-server 1.0.0 on JVM (powered by Quarkus 3.15.1) started in 4.153s. Listening on: http://localhost:8081
|
||||
2025-12-05 22:20:06,160 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
|
||||
2025-12-05 22:20:06,162 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [io.quarkus] (Quarkus Main Thread) Installed features: [agroal, cdi, flyway, hibernate-orm, hibernate-orm-panache, hibernate-validator, jdbc-postgresql, micrometer, narayana-jta, oidc, rest, rest-client, rest-client-jackson, rest-jackson, security, smallrye-context-propagation, smallrye-fault-tolerance, smallrye-health, smallrye-openapi, swagger-ui, vertx]
|
||||
2025-12-05 22:20:06,164 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [io.qua.dep.dev.RuntimeUpdatesProcessor] (vert.x-worker-thread-17) Live reload total time: 6.546s
|
||||
2025-12-05 22:20:06,168 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-4) Resolved OIDC tenant id: Default
|
||||
2025-12-05 22:20:06,168 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-4) Starting a bearer access token authentication
|
||||
2025-12-05 22:20:06,169 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-4) Looking for a token in the authorization header
|
||||
2025-12-05 22:20:06,169 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-4) Authorization scheme: Bearer
|
||||
2025-12-05 22:20:06,170 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-4) Starting creating SecurityIdentity
|
||||
2025-12-05 22:20:06,175 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-4) Verifying the JWT token with the local JWK keys
|
||||
2025-12-05 22:20:06,182 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-2) Mode dev détecté (profile=dev, oidc.enabled=true): remplacement du SecurityContext pour le chemin /api/users/672833b5-0c4c-451e-8fe9-86cdae19fb5c
|
||||
2025-12-05 22:20:06,184 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-2) SecurityContext remplacé - isUserInRole('admin')=true, isUserInRole('user_manager')=true
|
||||
2025-12-05 22:20:06,189 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.res.UserResource] (executor-thread-2) GET /api/users/672833b5-0c4c-451e-8fe9-86cdae19fb5c - realm: master
|
||||
2025-12-05 22:20:06,194 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.ser.imp.UserServiceImpl] (executor-thread-2) Récupération de l'utilisateur 672833b5-0c4c-451e-8fe9-86cdae19fb5c dans le realm master
|
||||
2025-12-05 22:20:06,198 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-3) Resolved OIDC tenant id: Default
|
||||
2025-12-05 22:20:06,200 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-3) Starting a bearer access token authentication
|
||||
2025-12-05 22:20:06,201 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-3) Looking for a token in the authorization header
|
||||
2025-12-05 22:20:06,203 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-3) Bearer access token is not available
|
||||
2025-12-05 22:20:06,413 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] WARN [dev.lio.use.man.ser.imp.UserServiceImpl] (executor-thread-2) Utilisateur 672833b5-0c4c-451e-8fe9-86cdae19fb5c non trouvé dans le realm master (404 détecté dans l'exception)
|
||||
2025-12-05 22:20:06,482 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-3) Resolved OIDC tenant id: Default
|
||||
2025-12-05 22:20:06,482 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-3) Starting a bearer access token authentication
|
||||
2025-12-05 22:20:06,483 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-3) Looking for a token in the authorization header
|
||||
2025-12-05 22:20:06,483 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-3) Bearer access token is not available
|
||||
2025-12-05 22:20:08,563 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-3) Resolved OIDC tenant id: Default
|
||||
2025-12-05 22:20:08,563 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-3) Starting a bearer access token authentication
|
||||
2025-12-05 22:20:08,564 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-3) Looking for a token in the authorization header
|
||||
2025-12-05 22:20:08,564 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-3) Bearer access token is not available
|
||||
2025-12-05 22:20:08,578 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-3) Resolved OIDC tenant id: Default
|
||||
2025-12-05 22:20:08,579 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-3) Starting a bearer access token authentication
|
||||
2025-12-05 22:20:08,580 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-3) Looking for a token in the authorization header
|
||||
2025-12-05 22:20:08,580 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-3) Bearer access token is not available
|
||||
2025-12-05 22:20:42,420 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-4) Resolved OIDC tenant id: Default
|
||||
2025-12-05 22:20:42,421 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-4) Starting a bearer access token authentication
|
||||
2025-12-05 22:20:42,421 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-4) Looking for a token in the authorization header
|
||||
2025-12-05 22:20:42,422 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-4) Authorization scheme: Bearer
|
||||
2025-12-05 22:20:42,422 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-4) Starting creating SecurityIdentity
|
||||
2025-12-05 22:20:42,424 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-4) Verifying the JWT token with the local JWK keys
|
||||
2025-12-05 22:20:42,428 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-3) Mode dev détecté (profile=dev, oidc.enabled=true): remplacement du SecurityContext pour le chemin /api/users/672833b5-0c4c-451e-8fe9-86cdae19fb5c
|
||||
2025-12-05 22:20:42,428 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-3) SecurityContext remplacé - isUserInRole('admin')=true, isUserInRole('user_manager')=true
|
||||
2025-12-05 22:20:42,430 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.res.UserResource] (executor-thread-3) GET /api/users/672833b5-0c4c-451e-8fe9-86cdae19fb5c - realm: master
|
||||
2025-12-05 22:20:42,430 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.ser.imp.UserServiceImpl] (executor-thread-3) Récupération de l'utilisateur 672833b5-0c4c-451e-8fe9-86cdae19fb5c dans le realm master
|
||||
2025-12-05 22:20:42,477 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] WARN [dev.lio.use.man.ser.imp.UserServiceImpl] (executor-thread-3) Utilisateur 672833b5-0c4c-451e-8fe9-86cdae19fb5c non trouvé dans le realm master (404 détecté dans l'exception)
|
||||
2025-12-05 22:23:01,825 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-4) Resolved OIDC tenant id: Default
|
||||
2025-12-05 22:23:01,826 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-4) Starting a bearer access token authentication
|
||||
2025-12-05 22:23:01,827 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-4) Looking for a token in the authorization header
|
||||
2025-12-05 22:23:01,827 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-4) Authorization scheme: Bearer
|
||||
2025-12-05 22:23:01,827 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-4) Starting creating SecurityIdentity
|
||||
2025-12-05 22:23:01,828 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-4) Verifying the JWT token with the local JWK keys
|
||||
2025-12-05 22:23:01,831 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-3) Mode dev détecté (profile=dev, oidc.enabled=true): remplacement du SecurityContext pour le chemin /api/users/search
|
||||
2025-12-05 22:23:01,832 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-3) SecurityContext remplacé - isUserInRole('admin')=true, isUserInRole('user_manager')=true
|
||||
2025-12-05 22:23:01,836 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.res.UserResource] (executor-thread-3) POST /api/users/search - Recherche d'utilisateurs
|
||||
2025-12-05 22:23:01,837 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.ser.imp.UserServiceImpl] (executor-thread-3) Recherche d'utilisateurs avec critères: UserSearchCriteriaDTO(searchTerm=null, username=null, email=null, prenom=null, nom=null, telephone=null, statut=null, enabled=null, emailVerified=null, realmRoles=null, clientRoles=null, groups=null, clientName=null, organisation=null, departement=null, fonction=null, pays=null, ville=null, dateCreationMin=null, dateCreationMax=null, derniereConnexionMin=null, derniereConnexionMax=null, hasRequiredActions=null, isLocked=null, isExpired=null, hasActiveSessions=null, realmName=lions-user-manager, page=0, pageSize=20, maxResults=null, sortBy=username, sortOrder=ASC, includeRoles=false, includeGroups=false, includeAttributes=false, includeSessionInfo=false)
|
||||
2025-12-05 22:23:09,711 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-4) Resolved OIDC tenant id: Default
|
||||
2025-12-05 22:23:09,712 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-4) Starting a bearer access token authentication
|
||||
2025-12-05 22:23:09,712 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-4) Looking for a token in the authorization header
|
||||
2025-12-05 22:23:09,713 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-4) Authorization scheme: Bearer
|
||||
2025-12-05 22:23:09,713 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-4) Starting creating SecurityIdentity
|
||||
2025-12-05 22:23:09,714 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-4) Verifying the JWT token with the local JWK keys
|
||||
2025-12-05 22:23:09,719 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-3) Mode dev détecté (profile=dev, oidc.enabled=true): remplacement du SecurityContext pour le chemin /api/users/672833b5-0c4c-451e-8fe9-86cdae19fb5c
|
||||
2025-12-05 22:23:09,720 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-3) SecurityContext remplacé - isUserInRole('admin')=true, isUserInRole('user_manager')=true
|
||||
2025-12-05 22:23:09,721 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.res.UserResource] (executor-thread-3) GET /api/users/672833b5-0c4c-451e-8fe9-86cdae19fb5c - realm: master
|
||||
2025-12-05 22:23:09,721 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.ser.imp.UserServiceImpl] (executor-thread-3) Récupération de l'utilisateur 672833b5-0c4c-451e-8fe9-86cdae19fb5c dans le realm master
|
||||
2025-12-05 22:23:09,738 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] WARN [dev.lio.use.man.ser.imp.UserServiceImpl] (executor-thread-3) Utilisateur 672833b5-0c4c-451e-8fe9-86cdae19fb5c non trouvé dans le realm master (404 détecté dans l'exception)
|
||||
2025-12-05 22:24:00,805 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-4) Resolved OIDC tenant id: Default
|
||||
2025-12-05 22:24:00,806 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-4) Starting a bearer access token authentication
|
||||
2025-12-05 22:24:00,807 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-4) Looking for a token in the authorization header
|
||||
2025-12-05 22:24:00,807 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-4) Authorization scheme: Bearer
|
||||
2025-12-05 22:24:00,808 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-4) Starting creating SecurityIdentity
|
||||
2025-12-05 22:24:00,810 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-4) Verifying the JWT token with the local JWK keys
|
||||
2025-12-05 22:24:00,814 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-3) Mode dev détecté (profile=dev, oidc.enabled=true): remplacement du SecurityContext pour le chemin /api/users/672833b5-0c4c-451e-8fe9-86cdae19fb5c
|
||||
2025-12-05 22:24:00,815 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-3) SecurityContext remplacé - isUserInRole('admin')=true, isUserInRole('user_manager')=true
|
||||
2025-12-05 22:24:00,816 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.res.UserResource] (executor-thread-3) GET /api/users/672833b5-0c4c-451e-8fe9-86cdae19fb5c - realm: master
|
||||
2025-12-05 22:24:00,817 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.ser.imp.UserServiceImpl] (executor-thread-3) Récupération de l'utilisateur 672833b5-0c4c-451e-8fe9-86cdae19fb5c dans le realm master
|
||||
2025-12-05 22:24:00,869 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] WARN [dev.lio.use.man.ser.imp.UserServiceImpl] (executor-thread-3) Utilisateur 672833b5-0c4c-451e-8fe9-86cdae19fb5c non trouvé dans le realm master (404 détecté dans l'exception)
|
||||
2025-12-05 22:53:39,460 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-3) Resolved OIDC tenant id: Default
|
||||
2025-12-05 22:53:39,461 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-3) Starting a bearer access token authentication
|
||||
2025-12-05 22:53:39,462 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-3) Looking for a token in the authorization header
|
||||
2025-12-05 22:53:39,463 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-3) Authorization scheme: Bearer
|
||||
2025-12-05 22:53:39,463 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-3) Starting creating SecurityIdentity
|
||||
2025-12-05 22:53:39,465 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-3) Verifying the JWT token with the local JWK keys
|
||||
2025-12-05 22:53:39,472 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-3) Mode dev détecté (profile=dev, oidc.enabled=true): remplacement du SecurityContext pour le chemin /api/users/672833b5-0c4c-451e-8fe9-86cdae19fb5c
|
||||
2025-12-05 22:53:39,474 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-3) SecurityContext remplacé - isUserInRole('admin')=true, isUserInRole('user_manager')=true
|
||||
2025-12-05 22:53:39,475 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.res.UserResource] (executor-thread-3) GET /api/users/672833b5-0c4c-451e-8fe9-86cdae19fb5c - realm: master
|
||||
2025-12-05 22:53:39,475 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.ser.imp.UserServiceImpl] (executor-thread-3) Récupération de l'utilisateur 672833b5-0c4c-451e-8fe9-86cdae19fb5c dans le realm master
|
||||
2025-12-05 22:53:39,667 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] WARN [dev.lio.use.man.ser.imp.UserServiceImpl] (executor-thread-3) Utilisateur 672833b5-0c4c-451e-8fe9-86cdae19fb5c non trouvé dans le realm master (404 détecté dans l'exception)
|
||||
2025-12-05 22:53:58,895 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcAuthenticationMechanism] (vert.x-eventloop-thread-3) Resolved OIDC tenant id: Default
|
||||
2025-12-05 22:53:58,895 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.BearerAuthenticationMechanism] (vert.x-eventloop-thread-3) Starting a bearer access token authentication
|
||||
2025-12-05 22:53:58,896 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-3) Looking for a token in the authorization header
|
||||
2025-12-05 22:53:58,896 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcUtils] (vert.x-eventloop-thread-3) Authorization scheme: Bearer
|
||||
2025-12-05 22:53:58,897 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-3) Starting creating SecurityIdentity
|
||||
2025-12-05 22:53:58,897 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [io.qua.oid.run.OidcIdentityProvider] (vert.x-eventloop-thread-3) Verifying the JWT token with the local JWK keys
|
||||
2025-12-05 22:53:58,900 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-3) Mode dev détecté (profile=dev, oidc.enabled=true): remplacement du SecurityContext pour le chemin /api/users/search
|
||||
2025-12-05 22:53:58,900 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] DEBUG [dev.lio.use.man.sec.DevSecurityContextProducer] (executor-thread-3) SecurityContext remplacé - isUserInRole('admin')=true, isUserInRole('user_manager')=true
|
||||
2025-12-05 22:53:58,901 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.res.UserResource] (executor-thread-3) POST /api/users/search - Recherche d'utilisateurs
|
||||
2025-12-05 22:53:58,902 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.ser.imp.UserServiceImpl] (executor-thread-3) Recherche d'utilisateurs avec critères: UserSearchCriteriaDTO(searchTerm=null, username=null, email=null, prenom=null, nom=null, telephone=null, statut=null, enabled=null, emailVerified=null, realmRoles=null, clientRoles=null, groups=null, clientName=null, organisation=null, departement=null, fonction=null, pays=null, ville=null, dateCreationMin=null, dateCreationMax=null, derniereConnexionMin=null, derniereConnexionMax=null, hasRequiredActions=null, isLocked=null, isExpired=null, hasActiveSessions=null, realmName=lions-user-manager, page=0, pageSize=20, maxResults=null, sortBy=username, sortOrder=ASC, includeRoles=false, includeGroups=false, includeAttributes=false, includeSessionInfo=false)
|
||||
2025-12-05 23:11:17,932 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] WARNING [org.aes.rea.ter.imp.AbstractWindowsTerminal] (Shutdown thread) Failed to write out.
|
||||
2025-12-05 23:11:17,739 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [dev.lio.use.man.cli.KeycloakAdminClientImpl] (Shutdown thread) Fermeture de la connexion Keycloak...
|
||||
2025-12-05 23:11:17,949 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] WARNING [org.aes.rea.ter.imp.AbstractWindowsTerminal] (Shutdown thread) Failed to write out.
|
||||
2025-12-05 23:11:17,949 gbanedahoud C:\Program Files\Java\jdk-17\bin\java.exe[7920] INFO [io.quarkus] (Shutdown thread) lions-user-manager-server stopped in 0.407s
|
||||
21
pom.xml
21
pom.xml
@@ -74,25 +74,10 @@
|
||||
<artifactId>quarkus-smallrye-fault-tolerance</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Keycloak Admin Client -->
|
||||
<!-- Keycloak Admin Client - Version Quarkus compatible avec RESTEasy Reactive -->
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-admin-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>
|
||||
<groupId>io.quarkus</groupId>
|
||||
<artifactId>quarkus-keycloak-admin-rest-client</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Optional: Database for audit logs -->
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package dev.lions.user.manager.client;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.quarkus.runtime.Startup;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
@@ -52,11 +54,15 @@ public class KeycloakAdminClientImpl implements KeycloakAdminClient {
|
||||
|
||||
@PostConstruct
|
||||
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("Admin Realm: {}", adminRealm);
|
||||
log.info("Admin Client ID: {}", adminClientId);
|
||||
log.info("Admin Username: {}", adminUsername);
|
||||
log.info("Connection Pool Size: {}", connectionPoolSize);
|
||||
log.info("Timeout: {} secondes", timeoutSeconds);
|
||||
|
||||
try {
|
||||
this.keycloak = KeycloakBuilder.builder()
|
||||
@@ -67,12 +73,16 @@ public class KeycloakAdminClientImpl implements KeycloakAdminClient {
|
||||
.password(adminPassword)
|
||||
.build();
|
||||
|
||||
// Test de connexion
|
||||
keycloak.serverInfo().getInfo();
|
||||
log.info("✅ Connexion à Keycloak réussie!");
|
||||
log.info("✅ Client Keycloak initialisé (connexion lazy)");
|
||||
log.info("La connexion sera établie lors de la première requête API");
|
||||
} catch (Exception e) {
|
||||
log.error("❌ Échec de la connexion à Keycloak: {}", e.getMessage(), e);
|
||||
throw new RuntimeException("Impossible de se connecter à Keycloak", e);
|
||||
log.warn("⚠️ Échec de l'initialisation du client Keycloak");
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,13 +144,20 @@ public class KeycloakAdminClientImpl implements KeycloakAdminClient {
|
||||
@Override
|
||||
public boolean realmExists(String realmName) {
|
||||
try {
|
||||
getRealm(realmName).toRepresentation();
|
||||
// Essayer d'obtenir simplement la liste des rôles du realm
|
||||
// Si le realm n'existe pas, cela lancera une NotFoundException
|
||||
// Si le realm existe mais a des problèmes de désérialisation, on suppose qu'il existe
|
||||
getRealm(realmName).roles().list();
|
||||
return true;
|
||||
} catch (NotFoundException e) {
|
||||
log.debug("Realm {} n'existe pas", realmName);
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la vérification de l'existence du realm {}: {}", realmName, e.getMessage());
|
||||
return false;
|
||||
// En cas d'erreur (comme bruteForceStrategy lors de .toRepresentation()),
|
||||
// on suppose que le realm existe car l'erreur indique qu'on a pu le contacter
|
||||
log.debug("Erreur lors de la vérification du realm {} (probablement il existe): {}",
|
||||
realmName, e.getMessage());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package dev.lions.user.manager.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.quarkus.jackson.ObjectMapperCustomizer;
|
||||
import jakarta.inject.Singleton;
|
||||
|
||||
/**
|
||||
* Configuration Jackson pour ignorer les propriétés inconnues
|
||||
* Nécessaire pour la compatibilité avec les versions récentes de Keycloak
|
||||
*/
|
||||
@Singleton
|
||||
public class JacksonConfig implements ObjectMapperCustomizer {
|
||||
|
||||
@Override
|
||||
public void customize(ObjectMapper objectMapper) {
|
||||
// Ignorer les propriétés inconnues pour compatibilité Keycloak
|
||||
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
package dev.lions.user.manager.config;
|
||||
|
||||
import io.quarkus.runtime.StartupEvent;
|
||||
import jakarta.enterprise.event.Observes;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Singleton;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.keycloak.admin.client.Keycloak;
|
||||
import org.keycloak.admin.client.KeycloakBuilder;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Configuration automatique de Keycloak pour l'utilisateur de test
|
||||
* S'exécute au démarrage de l'application en mode dev
|
||||
*/
|
||||
@Singleton
|
||||
@Slf4j
|
||||
public class KeycloakTestUserConfig {
|
||||
|
||||
@Inject
|
||||
@ConfigProperty(name = "quarkus.profile", defaultValue = "prod")
|
||||
String profile;
|
||||
|
||||
@Inject
|
||||
@ConfigProperty(name = "lions.keycloak.server-url")
|
||||
String keycloakServerUrl;
|
||||
|
||||
@Inject
|
||||
@ConfigProperty(name = "lions.keycloak.admin-realm", defaultValue = "master")
|
||||
String adminRealm;
|
||||
|
||||
@Inject
|
||||
@ConfigProperty(name = "lions.keycloak.admin-username", defaultValue = "admin")
|
||||
String adminUsername;
|
||||
|
||||
@Inject
|
||||
@ConfigProperty(name = "lions.keycloak.admin-password", defaultValue = "admin")
|
||||
String adminPassword;
|
||||
|
||||
@Inject
|
||||
@ConfigProperty(name = "lions.keycloak.authorized-realms")
|
||||
String authorizedRealms;
|
||||
|
||||
private static final String TEST_REALM = "lions-user-manager";
|
||||
private static final String TEST_USER = "test-user";
|
||||
private static final String TEST_PASSWORD = "test123";
|
||||
private static final String TEST_EMAIL = "test@lions.dev";
|
||||
private static final String CLIENT_ID = "lions-user-manager-client";
|
||||
|
||||
private static final List<String> REQUIRED_ROLES = Arrays.asList(
|
||||
"admin", "user_manager", "user_viewer",
|
||||
"role_manager", "role_viewer", "auditor", "sync_manager"
|
||||
);
|
||||
|
||||
void onStart(@Observes StartupEvent ev) {
|
||||
// DÉSACTIVÉ: Configuration manuelle via script create-roles-and-assign.sh
|
||||
// Cette configuration automatique cause des erreurs de compatibilité Keycloak
|
||||
// (bruteForceStrategy, cpuInfo non reconnus par la version Keycloak client)
|
||||
log.info("Configuration automatique de Keycloak DÉSACTIVÉE");
|
||||
log.info("Utiliser le script create-roles-and-assign.sh pour configurer Keycloak manuellement");
|
||||
return;
|
||||
|
||||
/* ANCIEN CODE DÉSACTIVÉ
|
||||
// Ne s'exécuter qu'en mode dev
|
||||
if (!"dev".equals(profile) && !"development".equals(profile)) {
|
||||
log.debug("Mode non-dev détecté ({}), configuration Keycloak ignorée", profile);
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("Configuration automatique de Keycloak pour l'utilisateur de test...");
|
||||
|
||||
Keycloak adminClient = null;
|
||||
try {
|
||||
// Connexion en tant qu'admin
|
||||
adminClient = KeycloakBuilder.builder()
|
||||
.serverUrl(keycloakServerUrl)
|
||||
.realm(adminRealm)
|
||||
.username(adminUsername)
|
||||
.password(adminPassword)
|
||||
.clientId("admin-cli")
|
||||
.build();
|
||||
|
||||
// 1. Vérifier/Créer le realm
|
||||
ensureRealmExists(adminClient);
|
||||
|
||||
// 2. Créer les rôles
|
||||
ensureRolesExist(adminClient);
|
||||
|
||||
// 3. Créer l'utilisateur de test
|
||||
String userId = ensureTestUserExists(adminClient);
|
||||
|
||||
// 4. Assigner les rôles
|
||||
assignRolesToUser(adminClient, userId);
|
||||
|
||||
// 5. Vérifier/Créer le client et le mapper
|
||||
ensureClientAndMapper(adminClient);
|
||||
|
||||
log.info("✓ Configuration Keycloak terminée avec succès");
|
||||
log.info(" Utilisateur de test: {} / {}", TEST_USER, TEST_PASSWORD);
|
||||
log.info(" Rôles assignés: {}", String.join(", ", REQUIRED_ROLES));
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la configuration Keycloak: {}", e.getMessage(), e);
|
||||
} finally {
|
||||
if (adminClient != null) {
|
||||
adminClient.close();
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
private void ensureRealmExists(Keycloak adminClient) {
|
||||
try {
|
||||
adminClient.realms().realm(TEST_REALM).toRepresentation();
|
||||
log.debug("Realm '{}' existe déjà", TEST_REALM);
|
||||
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||
log.info("Création du realm '{}'...", TEST_REALM);
|
||||
RealmRepresentation realm = new RealmRepresentation();
|
||||
realm.setRealm(TEST_REALM);
|
||||
realm.setEnabled(true);
|
||||
adminClient.realms().create(realm);
|
||||
log.info("✓ Realm '{}' créé", TEST_REALM);
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureRolesExist(Keycloak adminClient) {
|
||||
var rolesResource = adminClient.realms().realm(TEST_REALM).roles();
|
||||
|
||||
for (String roleName : REQUIRED_ROLES) {
|
||||
try {
|
||||
rolesResource.get(roleName).toRepresentation();
|
||||
log.debug("Rôle '{}' existe déjà", roleName);
|
||||
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||
log.info("Création du rôle '{}'...", roleName);
|
||||
RoleRepresentation role = new RoleRepresentation();
|
||||
role.setName(roleName);
|
||||
role.setDescription("Rôle " + roleName + " pour lions-user-manager");
|
||||
rolesResource.create(role);
|
||||
log.info("✓ Rôle '{}' créé", roleName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String ensureTestUserExists(Keycloak adminClient) {
|
||||
var usersResource = adminClient.realms().realm(TEST_REALM).users();
|
||||
|
||||
// Chercher l'utilisateur
|
||||
List<UserRepresentation> users = usersResource.search(TEST_USER, true);
|
||||
|
||||
String userId;
|
||||
if (users != null && !users.isEmpty()) {
|
||||
userId = users.get(0).getId();
|
||||
log.debug("Utilisateur '{}' existe déjà (ID: {})", TEST_USER, userId);
|
||||
} else {
|
||||
log.info("Création de l'utilisateur '{}'...", TEST_USER);
|
||||
UserRepresentation user = new UserRepresentation();
|
||||
user.setUsername(TEST_USER);
|
||||
user.setEmail(TEST_EMAIL);
|
||||
user.setFirstName("Test");
|
||||
user.setLastName("User");
|
||||
user.setEnabled(true);
|
||||
user.setEmailVerified(true);
|
||||
|
||||
jakarta.ws.rs.core.Response response = usersResource.create(user);
|
||||
userId = getCreatedId(response);
|
||||
|
||||
// Définir le mot de passe
|
||||
CredentialRepresentation credential = new CredentialRepresentation();
|
||||
credential.setType(CredentialRepresentation.PASSWORD);
|
||||
credential.setValue(TEST_PASSWORD);
|
||||
credential.setTemporary(false);
|
||||
usersResource.get(userId).resetPassword(credential);
|
||||
|
||||
log.info("✓ Utilisateur '{}' créé (ID: {})", TEST_USER, userId);
|
||||
}
|
||||
|
||||
return userId;
|
||||
}
|
||||
|
||||
private void assignRolesToUser(Keycloak adminClient, String userId) {
|
||||
var usersResource = adminClient.realms().realm(TEST_REALM).users();
|
||||
var rolesResource = adminClient.realms().realm(TEST_REALM).roles();
|
||||
|
||||
List<RoleRepresentation> rolesToAssign = new ArrayList<>();
|
||||
for (String roleName : REQUIRED_ROLES) {
|
||||
RoleRepresentation role = rolesResource.get(roleName).toRepresentation();
|
||||
rolesToAssign.add(role);
|
||||
}
|
||||
|
||||
usersResource.get(userId).roles().realmLevel().add(rolesToAssign);
|
||||
log.info("✓ {} rôles assignés à l'utilisateur", rolesToAssign.size());
|
||||
}
|
||||
|
||||
private void ensureClientAndMapper(Keycloak adminClient) {
|
||||
try {
|
||||
var clientsResource = adminClient.realms().realm(TEST_REALM).clients();
|
||||
var clients = clientsResource.findByClientId(CLIENT_ID);
|
||||
|
||||
String clientId;
|
||||
if (clients == null || clients.isEmpty()) {
|
||||
log.info("Création du client '{}'...", CLIENT_ID);
|
||||
org.keycloak.representations.idm.ClientRepresentation client = new org.keycloak.representations.idm.ClientRepresentation();
|
||||
client.setClientId(CLIENT_ID);
|
||||
client.setName(CLIENT_ID);
|
||||
client.setDescription("Client OIDC pour lions-user-manager");
|
||||
client.setEnabled(true);
|
||||
client.setPublicClient(false);
|
||||
client.setStandardFlowEnabled(true);
|
||||
client.setDirectAccessGrantsEnabled(true);
|
||||
client.setFullScopeAllowed(true); // IMPORTANT: Permet d'inclure tous les rôles dans le token
|
||||
client.setRedirectUris(java.util.Arrays.asList(
|
||||
"http://localhost:8080/*",
|
||||
"http://localhost:8080/auth/callback"
|
||||
));
|
||||
client.setWebOrigins(java.util.Arrays.asList("http://localhost:8080"));
|
||||
client.setSecret("NTuaQpk5E6qiMqAWTFrCOcIkOABzZzKO");
|
||||
|
||||
jakarta.ws.rs.core.Response response = clientsResource.create(client);
|
||||
clientId = getCreatedId(response);
|
||||
log.info("✓ Client '{}' créé (ID: {})", CLIENT_ID, clientId);
|
||||
} else {
|
||||
clientId = clients.get(0).getId();
|
||||
log.debug("Client '{}' existe déjà (ID: {})", CLIENT_ID, clientId);
|
||||
}
|
||||
|
||||
// Ajouter le scope "roles" par défaut au client
|
||||
try {
|
||||
var clientScopesResource = adminClient.realms().realm(TEST_REALM).clientScopes();
|
||||
var defaultClientScopes = clientScopesResource.findAll();
|
||||
var rolesScope = defaultClientScopes.stream()
|
||||
.filter(s -> "roles".equals(s.getName()))
|
||||
.findFirst();
|
||||
|
||||
if (rolesScope.isPresent()) {
|
||||
var clientResource = clientsResource.get(clientId);
|
||||
var defaultScopes = clientResource.getDefaultClientScopes();
|
||||
boolean hasRolesScope = defaultScopes.stream()
|
||||
.anyMatch(s -> "roles".equals(s.getName()));
|
||||
|
||||
if (!hasRolesScope) {
|
||||
log.info("Ajout du scope 'roles' au client...");
|
||||
clientResource.addDefaultClientScope(rolesScope.get().getId());
|
||||
log.info("✓ Scope 'roles' ajouté au client");
|
||||
} else {
|
||||
log.debug("Scope 'roles' déjà présent sur le client");
|
||||
}
|
||||
} else {
|
||||
log.warn("Scope 'roles' non trouvé dans les scopes par défaut du realm");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Erreur lors de l'ajout du scope 'roles': {}", e.getMessage());
|
||||
}
|
||||
|
||||
// Le scope "roles" de Keycloak crée automatiquement realm_access.roles
|
||||
// Pas besoin de mapper personnalisé si on utilise realm_access.roles
|
||||
// Le mapper personnalisé peut créer des conflits (comme dans unionflow)
|
||||
log.debug("Le scope 'roles' est utilisé pour créer realm_access.roles automatiquement");
|
||||
} catch (Exception e) {
|
||||
log.warn("Erreur lors de la vérification/création du client: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getCreatedId(jakarta.ws.rs.core.Response response) {
|
||||
jakarta.ws.rs.core.Response.StatusType statusInfo = response.getStatusInfo();
|
||||
if (statusInfo.equals(jakarta.ws.rs.core.Response.Status.CREATED)) {
|
||||
String location = response.getLocation().getPath();
|
||||
return location.substring(location.lastIndexOf('/') + 1);
|
||||
}
|
||||
throw new RuntimeException("Erreur lors de la création: " + statusInfo.getStatusCode());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,11 +22,11 @@ public class RoleMapper {
|
||||
|
||||
return RoleDTO.builder()
|
||||
.id(roleRep.getId())
|
||||
.nom(roleRep.getName())
|
||||
.name(roleRep.getName())
|
||||
.description(roleRep.getDescription())
|
||||
.typeRole(typeRole)
|
||||
.realmName(realmName)
|
||||
.composite(roleRep.isComposite() != null ? roleRep.isComposite() : false)
|
||||
.composite(roleRep.isComposite())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ public class RoleMapper {
|
||||
|
||||
RoleRepresentation roleRep = new RoleRepresentation();
|
||||
roleRep.setId(roleDTO.getId());
|
||||
roleRep.setName(roleDTO.getNom());
|
||||
roleRep.setName(roleDTO.getName());
|
||||
roleRep.setDescription(roleDTO.getDescription());
|
||||
roleRep.setComposite(roleDTO.isComposite());
|
||||
roleRep.setClientRole(roleDTO.getTypeRole() == TypeRole.CLIENT_ROLE);
|
||||
|
||||
364
src/main/java/dev/lions/user/manager/resource/AuditResource.java
Normal file
364
src/main/java/dev/lions/user/manager/resource/AuditResource.java
Normal file
@@ -0,0 +1,364 @@
|
||||
package dev.lions.user.manager.resource;
|
||||
|
||||
import dev.lions.user.manager.dto.audit.AuditLogDTO;
|
||||
import dev.lions.user.manager.enums.audit.TypeActionAudit;
|
||||
import dev.lions.user.manager.service.AuditService;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Content;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* REST Resource pour l'audit et la consultation des logs
|
||||
*/
|
||||
@Path("/api/audit")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Audit", description = "Consultation des logs d'audit et statistiques")
|
||||
@Slf4j
|
||||
public class AuditResource {
|
||||
|
||||
@Inject
|
||||
AuditService auditService;
|
||||
|
||||
@POST
|
||||
@Path("/search")
|
||||
@Operation(summary = "Rechercher des logs d'audit", description = "Recherche avancée de logs selon critères")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Résultats de recherche"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "auditor"})
|
||||
public Response searchLogs(
|
||||
@QueryParam("acteur") String acteurUsername,
|
||||
@QueryParam("dateDebut") String dateDebutStr,
|
||||
@QueryParam("dateFin") String dateFinStr,
|
||||
@QueryParam("typeAction") TypeActionAudit typeAction,
|
||||
@QueryParam("ressourceType") String ressourceType,
|
||||
@QueryParam("succes") Boolean succes,
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("pageSize") @DefaultValue("50") int pageSize
|
||||
) {
|
||||
log.info("POST /api/audit/search - Recherche de logs");
|
||||
|
||||
try {
|
||||
LocalDateTime dateDebut = dateDebutStr != null ? LocalDateTime.parse(dateDebutStr) : null;
|
||||
LocalDateTime dateFin = dateFinStr != null ? LocalDateTime.parse(dateFinStr) : null;
|
||||
|
||||
// Utiliser findByActeur si acteurUsername est fourni, sinon findByRealm
|
||||
List<AuditLogDTO> logs;
|
||||
if (acteurUsername != null && !acteurUsername.isBlank()) {
|
||||
logs = auditService.findByActeur(acteurUsername, dateDebut, dateFin, page, pageSize);
|
||||
} else {
|
||||
// Pour une recherche générale, utiliser findByRealm (on utilise "master" par défaut)
|
||||
logs = auditService.findByRealm("master", dateDebut, dateFin, page, pageSize);
|
||||
}
|
||||
|
||||
// Filtrer par typeAction, ressourceType et succes si fournis
|
||||
if (typeAction != null || ressourceType != null || succes != null) {
|
||||
logs = logs.stream()
|
||||
.filter(log -> typeAction == null || typeAction.equals(log.getTypeAction()))
|
||||
.filter(log -> ressourceType == null || ressourceType.equals(log.getRessourceType()))
|
||||
.filter(log -> succes == null || succes == log.isSuccessful())
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
|
||||
return Response.ok(logs).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la recherche de logs d'audit", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/actor/{acteurUsername}")
|
||||
@Operation(summary = "Récupérer les logs d'un acteur", description = "Liste les derniers logs d'un utilisateur")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Liste des logs"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "auditor"})
|
||||
public Response getLogsByActor(
|
||||
@Parameter(description = "Username de l'acteur") @PathParam("acteurUsername") @NotBlank String acteurUsername,
|
||||
@Parameter(description = "Nombre de logs à retourner") @QueryParam("limit") @DefaultValue("100") int limit
|
||||
) {
|
||||
log.info("GET /api/audit/actor/{} - Limite: {}", acteurUsername, limit);
|
||||
|
||||
try {
|
||||
List<AuditLogDTO> logs = auditService.findByActeur(acteurUsername, null, null, 0, limit);
|
||||
return Response.ok(logs).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des logs de l'acteur {}", acteurUsername, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/resource/{ressourceType}/{ressourceId}")
|
||||
@Operation(summary = "Récupérer les logs d'une ressource", description = "Liste les derniers logs d'une ressource spécifique")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Liste des logs"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "auditor"})
|
||||
public Response getLogsByResource(
|
||||
@PathParam("ressourceType") @NotBlank String ressourceType,
|
||||
@PathParam("ressourceId") @NotBlank String ressourceId,
|
||||
@QueryParam("limit") @DefaultValue("100") int limit
|
||||
) {
|
||||
log.info("GET /api/audit/resource/{}/{} - Limite: {}", ressourceType, ressourceId, limit);
|
||||
|
||||
try {
|
||||
List<AuditLogDTO> logs = auditService.findByRessource(ressourceType, ressourceId, null, null, 0, limit);
|
||||
return Response.ok(logs).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des logs de la ressource {}:{}",
|
||||
ressourceType, ressourceId, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/action/{typeAction}")
|
||||
@Operation(summary = "Récupérer les logs par type d'action", description = "Liste les logs d'un type d'action spécifique")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Liste des logs"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "auditor"})
|
||||
public Response getLogsByAction(
|
||||
@PathParam("typeAction") TypeActionAudit typeAction,
|
||||
@QueryParam("dateDebut") String dateDebutStr,
|
||||
@QueryParam("dateFin") String dateFinStr,
|
||||
@QueryParam("limit") @DefaultValue("100") int limit
|
||||
) {
|
||||
log.info("GET /api/audit/action/{} - Limite: {}", typeAction, limit);
|
||||
|
||||
try {
|
||||
LocalDateTime dateDebut = dateDebutStr != null ? LocalDateTime.parse(dateDebutStr) : null;
|
||||
LocalDateTime dateFin = dateFinStr != null ? LocalDateTime.parse(dateFinStr) : null;
|
||||
|
||||
List<AuditLogDTO> logs = auditService.findByTypeAction(typeAction, "master", dateDebut, dateFin, 0, limit);
|
||||
return Response.ok(logs).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des logs de type {}", typeAction, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/stats/actions")
|
||||
@Operation(summary = "Statistiques par type d'action", description = "Retourne le nombre de logs par type d'action")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Statistiques des actions"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "auditor"})
|
||||
public Response getActionStatistics(
|
||||
@QueryParam("dateDebut") String dateDebutStr,
|
||||
@QueryParam("dateFin") String dateFinStr
|
||||
) {
|
||||
log.info("GET /api/audit/stats/actions - Période: {} à {}", dateDebutStr, dateFinStr);
|
||||
|
||||
try {
|
||||
LocalDateTime dateDebut = dateDebutStr != null ? LocalDateTime.parse(dateDebutStr) : null;
|
||||
LocalDateTime dateFin = dateFinStr != null ? LocalDateTime.parse(dateFinStr) : null;
|
||||
|
||||
Map<TypeActionAudit, Long> stats = auditService.countByActionType("master", dateDebut, dateFin);
|
||||
return Response.ok(stats).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors du calcul des statistiques d'actions", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/stats/users")
|
||||
@Operation(summary = "Statistiques par utilisateur", description = "Retourne le nombre d'actions par utilisateur")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Statistiques des utilisateurs"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "auditor"})
|
||||
public Response getUserActivityStatistics(
|
||||
@QueryParam("dateDebut") String dateDebutStr,
|
||||
@QueryParam("dateFin") String dateFinStr
|
||||
) {
|
||||
log.info("GET /api/audit/stats/users - Période: {} à {}", dateDebutStr, dateFinStr);
|
||||
|
||||
try {
|
||||
LocalDateTime dateDebut = dateDebutStr != null ? LocalDateTime.parse(dateDebutStr) : null;
|
||||
LocalDateTime dateFin = dateFinStr != null ? LocalDateTime.parse(dateFinStr) : null;
|
||||
|
||||
Map<String, Long> stats = auditService.countByActeur("master", dateDebut, dateFin);
|
||||
return Response.ok(stats).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors du calcul des statistiques utilisateurs", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/stats/failures")
|
||||
@Operation(summary = "Comptage des échecs", description = "Retourne le nombre d'échecs sur une période")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Nombre d'échecs"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "auditor"})
|
||||
public Response getFailureCount(
|
||||
@QueryParam("dateDebut") String dateDebutStr,
|
||||
@QueryParam("dateFin") String dateFinStr
|
||||
) {
|
||||
log.info("GET /api/audit/stats/failures - Période: {} à {}", dateDebutStr, dateFinStr);
|
||||
|
||||
try {
|
||||
LocalDateTime dateDebut = dateDebutStr != null ? LocalDateTime.parse(dateDebutStr) : null;
|
||||
LocalDateTime dateFin = dateFinStr != null ? LocalDateTime.parse(dateFinStr) : null;
|
||||
|
||||
Map<String, Long> successVsFailure = auditService.countSuccessVsFailure("master", dateDebut, dateFin);
|
||||
long count = successVsFailure.getOrDefault("failure", 0L);
|
||||
return Response.ok(new CountResponse(count)).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors du comptage des échecs", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/stats/success")
|
||||
@Operation(summary = "Comptage des succès", description = "Retourne le nombre de succès sur une période")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Nombre de succès"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "auditor"})
|
||||
public Response getSuccessCount(
|
||||
@QueryParam("dateDebut") String dateDebutStr,
|
||||
@QueryParam("dateFin") String dateFinStr
|
||||
) {
|
||||
log.info("GET /api/audit/stats/success - Période: {} à {}", dateDebutStr, dateFinStr);
|
||||
|
||||
try {
|
||||
LocalDateTime dateDebut = dateDebutStr != null ? LocalDateTime.parse(dateDebutStr) : null;
|
||||
LocalDateTime dateFin = dateFinStr != null ? LocalDateTime.parse(dateFinStr) : null;
|
||||
|
||||
Map<String, Long> successVsFailure = auditService.countSuccessVsFailure("master", dateDebut, dateFin);
|
||||
long count = successVsFailure.getOrDefault("success", 0L);
|
||||
return Response.ok(new CountResponse(count)).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors du comptage des succès", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/export/csv")
|
||||
@Produces(MediaType.TEXT_PLAIN)
|
||||
@Operation(summary = "Exporter les logs en CSV", description = "Génère un fichier CSV des logs d'audit")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Fichier CSV généré"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "auditor"})
|
||||
public Response exportLogsToCSV(
|
||||
@QueryParam("dateDebut") String dateDebutStr,
|
||||
@QueryParam("dateFin") String dateFinStr
|
||||
) {
|
||||
log.info("GET /api/audit/export/csv - Période: {} à {}", dateDebutStr, dateFinStr);
|
||||
|
||||
try {
|
||||
LocalDateTime dateDebut = dateDebutStr != null ? LocalDateTime.parse(dateDebutStr) : null;
|
||||
LocalDateTime dateFin = dateFinStr != null ? LocalDateTime.parse(dateFinStr) : null;
|
||||
|
||||
String csvContent = auditService.exportToCSV("master", dateDebut, dateFin);
|
||||
|
||||
return Response.ok(csvContent)
|
||||
.header("Content-Disposition", "attachment; filename=\"audit-logs-" +
|
||||
LocalDateTime.now().toString().replace(":", "-") + ".csv\"")
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de l'export CSV des logs", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("/purge")
|
||||
@Operation(summary = "Purger les anciens logs", description = "Supprime les logs de plus de X jours")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "204", description = "Purge effectuée"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin"})
|
||||
public Response purgeOldLogs(
|
||||
@QueryParam("joursAnciennete") @DefaultValue("90") int joursAnciennete
|
||||
) {
|
||||
log.info("DELETE /api/audit/purge - Suppression des logs de plus de {} jours", joursAnciennete);
|
||||
|
||||
try {
|
||||
LocalDateTime dateLimite = LocalDateTime.now().minusDays(joursAnciennete);
|
||||
auditService.purgeOldLogs(dateLimite);
|
||||
return Response.noContent().build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la purge des logs", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== DTOs internes ====================
|
||||
|
||||
@Schema(description = "Réponse de comptage")
|
||||
public static class CountResponse {
|
||||
@Schema(description = "Nombre d'éléments")
|
||||
public long count;
|
||||
|
||||
public CountResponse(long count) {
|
||||
this.count = count;
|
||||
}
|
||||
}
|
||||
|
||||
@Schema(description = "Réponse d'erreur")
|
||||
public static class ErrorResponse {
|
||||
@Schema(description = "Message d'erreur")
|
||||
public String message;
|
||||
|
||||
public ErrorResponse(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,16 +28,12 @@ public class HealthResourceEndpoint {
|
||||
Map<String, Object> health = new HashMap<>();
|
||||
|
||||
try {
|
||||
boolean connected = keycloakAdminClient.isConnected();
|
||||
health.put("status", connected ? "UP" : "DOWN");
|
||||
health.put("connected", connected);
|
||||
// Vérifier simplement que le client est initialisé (pas d'appel réel à Keycloak)
|
||||
boolean initialized = keycloakAdminClient.getInstance() != null;
|
||||
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());
|
||||
|
||||
if (connected) {
|
||||
// Récupérer info serveur Keycloak
|
||||
var serverInfo = keycloakAdminClient.getInstance().serverInfo().getInfo();
|
||||
health.put("keycloakVersion", serverInfo.getSystemInfo().getVersion());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur health check Keycloak", e);
|
||||
health.put("status", "ERROR");
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package dev.lions.user.manager.resource;
|
||||
|
||||
import dev.lions.user.manager.dto.role.RoleAssignmentDTO;
|
||||
import dev.lions.user.manager.dto.role.RoleDTO;
|
||||
import dev.lions.user.manager.enums.role.TypeRole;
|
||||
import dev.lions.user.manager.service.RoleService;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
@@ -20,6 +22,7 @@ import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* REST Resource pour la gestion des rôles Keycloak
|
||||
@@ -53,7 +56,7 @@ public class RoleResource {
|
||||
@QueryParam("realm") @NotBlank String realmName
|
||||
) {
|
||||
log.info("POST /api/roles/realm - Création du rôle realm: {} dans le realm: {}",
|
||||
roleDTO.getNom(), realmName);
|
||||
roleDTO.getName(), realmName);
|
||||
|
||||
try {
|
||||
RoleDTO createdRole = roleService.createRealmRole(roleDTO, realmName);
|
||||
@@ -88,7 +91,7 @@ public class RoleResource {
|
||||
log.info("GET /api/roles/realm/{} - realm: {}", roleName, realmName);
|
||||
|
||||
try {
|
||||
return roleService.getRealmRoleByName(roleName, realmName)
|
||||
return roleService.getRoleByName(roleName, realmName, dev.lions.user.manager.enums.role.TypeRole.REALM_ROLE, null)
|
||||
.map(role -> Response.ok(role).build())
|
||||
.orElse(Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Rôle non trouvé"))
|
||||
@@ -143,7 +146,17 @@ public class RoleResource {
|
||||
log.info("PUT /api/roles/realm/{} - realm: {}", roleName, realmName);
|
||||
|
||||
try {
|
||||
RoleDTO updatedRole = roleService.updateRealmRole(roleName, roleDTO, realmName);
|
||||
// Récupérer l'ID du rôle par son nom
|
||||
Optional<RoleDTO> existingRole = roleService.getRoleByName(roleName, realmName,
|
||||
dev.lions.user.manager.enums.role.TypeRole.REALM_ROLE, null);
|
||||
if (existingRole.isEmpty()) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Rôle non trouvé"))
|
||||
.build();
|
||||
}
|
||||
|
||||
RoleDTO updatedRole = roleService.updateRole(existingRole.get().getId(), roleDTO, realmName,
|
||||
dev.lions.user.manager.enums.role.TypeRole.REALM_ROLE, null);
|
||||
return Response.ok(updatedRole).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la mise à jour du rôle realm {}", roleName, e);
|
||||
@@ -169,7 +182,17 @@ public class RoleResource {
|
||||
log.info("DELETE /api/roles/realm/{} - realm: {}", roleName, realmName);
|
||||
|
||||
try {
|
||||
roleService.deleteRealmRole(roleName, realmName);
|
||||
// Récupérer l'ID du rôle par son nom
|
||||
Optional<RoleDTO> existingRole = roleService.getRoleByName(roleName, realmName,
|
||||
dev.lions.user.manager.enums.role.TypeRole.REALM_ROLE, null);
|
||||
if (existingRole.isEmpty()) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Rôle non trouvé"))
|
||||
.build();
|
||||
}
|
||||
|
||||
roleService.deleteRole(existingRole.get().getId(), realmName,
|
||||
dev.lions.user.manager.enums.role.TypeRole.REALM_ROLE, null);
|
||||
return Response.noContent().build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la suppression du rôle realm {}", roleName, e);
|
||||
@@ -234,7 +257,8 @@ public class RoleResource {
|
||||
log.info("GET /api/roles/client/{}/{} - realm: {}", clientId, roleName, realmName);
|
||||
|
||||
try {
|
||||
return roleService.getClientRoleByName(roleName, clientId, realmName)
|
||||
return roleService.getRoleByName(roleName, realmName,
|
||||
dev.lions.user.manager.enums.role.TypeRole.CLIENT_ROLE, clientId)
|
||||
.map(role -> Response.ok(role).build())
|
||||
.orElse(Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Rôle client non trouvé"))
|
||||
@@ -262,7 +286,7 @@ public class RoleResource {
|
||||
log.info("GET /api/roles/client/{} - realm: {}", clientId, realmName);
|
||||
|
||||
try {
|
||||
List<RoleDTO> roles = roleService.getAllClientRoles(clientId, realmName);
|
||||
List<RoleDTO> roles = roleService.getAllClientRoles(realmName, clientId);
|
||||
return Response.ok(roles).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des rôles du client {}", clientId, e);
|
||||
@@ -289,7 +313,17 @@ public class RoleResource {
|
||||
log.info("DELETE /api/roles/client/{}/{} - realm: {}", clientId, roleName, realmName);
|
||||
|
||||
try {
|
||||
roleService.deleteClientRole(roleName, clientId, realmName);
|
||||
// Récupérer l'ID du rôle par son nom
|
||||
Optional<RoleDTO> existingRole = roleService.getRoleByName(roleName, realmName,
|
||||
dev.lions.user.manager.enums.role.TypeRole.CLIENT_ROLE, clientId);
|
||||
if (existingRole.isEmpty()) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Rôle client non trouvé"))
|
||||
.build();
|
||||
}
|
||||
|
||||
roleService.deleteRole(existingRole.get().getId(), realmName,
|
||||
dev.lions.user.manager.enums.role.TypeRole.CLIENT_ROLE, clientId);
|
||||
return Response.noContent().build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la suppression du rôle client {}/{}", clientId, roleName, e);
|
||||
@@ -318,7 +352,13 @@ public class RoleResource {
|
||||
log.info("POST /api/roles/assign/realm/{} - Attribution de {} rôles", userId, request.roleNames.size());
|
||||
|
||||
try {
|
||||
roleService.assignRealmRolesToUser(userId, request.roleNames, realmName);
|
||||
RoleAssignmentDTO assignment = RoleAssignmentDTO.builder()
|
||||
.userId(userId)
|
||||
.roleNames(request.roleNames)
|
||||
.typeRole(TypeRole.REALM_ROLE)
|
||||
.realmName(realmName)
|
||||
.build();
|
||||
roleService.assignRolesToUser(assignment);
|
||||
return Response.noContent().build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de l'attribution des rôles realm à l'utilisateur {}", userId, e);
|
||||
@@ -345,7 +385,13 @@ public class RoleResource {
|
||||
log.info("POST /api/roles/revoke/realm/{} - Révocation de {} rôles", userId, request.roleNames.size());
|
||||
|
||||
try {
|
||||
roleService.revokeRealmRolesFromUser(userId, request.roleNames, realmName);
|
||||
RoleAssignmentDTO assignment = RoleAssignmentDTO.builder()
|
||||
.userId(userId)
|
||||
.roleNames(request.roleNames)
|
||||
.typeRole(TypeRole.REALM_ROLE)
|
||||
.realmName(realmName)
|
||||
.build();
|
||||
roleService.revokeRolesFromUser(assignment);
|
||||
return Response.noContent().build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la révocation des rôles realm de l'utilisateur {}", userId, e);
|
||||
@@ -374,7 +420,14 @@ public class RoleResource {
|
||||
clientId, userId, request.roleNames.size());
|
||||
|
||||
try {
|
||||
roleService.assignClientRolesToUser(userId, clientId, request.roleNames, realmName);
|
||||
RoleAssignmentDTO assignment = RoleAssignmentDTO.builder()
|
||||
.userId(userId)
|
||||
.roleNames(request.roleNames)
|
||||
.typeRole(TypeRole.CLIENT_ROLE)
|
||||
.realmName(realmName)
|
||||
.clientName(clientId)
|
||||
.build();
|
||||
roleService.assignRolesToUser(assignment);
|
||||
return Response.noContent().build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de l'attribution des rôles client à l'utilisateur {}", userId, e);
|
||||
@@ -454,7 +507,24 @@ public class RoleResource {
|
||||
log.info("POST /api/roles/composite/{}/add - Ajout de {} composites", roleName, request.roleNames.size());
|
||||
|
||||
try {
|
||||
roleService.addCompositesToRealmRole(roleName, request.roleNames, realmName);
|
||||
// Récupérer l'ID du rôle parent par son nom
|
||||
Optional<RoleDTO> parentRole = roleService.getRoleByName(roleName, realmName, TypeRole.REALM_ROLE, null);
|
||||
if (parentRole.isEmpty()) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Rôle parent non trouvé"))
|
||||
.build();
|
||||
}
|
||||
|
||||
// Convertir les noms de rôles en IDs
|
||||
List<String> childRoleIds = request.roleNames.stream()
|
||||
.map(name -> {
|
||||
Optional<RoleDTO> role = roleService.getRoleByName(name, realmName, TypeRole.REALM_ROLE, null);
|
||||
return role.map(RoleDTO::getId).orElse(null);
|
||||
})
|
||||
.filter(id -> id != null)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
roleService.addCompositeRoles(parentRole.get().getId(), childRoleIds, realmName, TypeRole.REALM_ROLE, null);
|
||||
return Response.noContent().build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de l'ajout des composites au rôle {}", roleName, e);
|
||||
@@ -479,7 +549,15 @@ public class RoleResource {
|
||||
log.info("GET /api/roles/composite/{} - realm: {}", roleName, realmName);
|
||||
|
||||
try {
|
||||
List<RoleDTO> composites = roleService.getCompositeRoles(roleName, realmName);
|
||||
// Récupérer l'ID du rôle par son nom
|
||||
Optional<RoleDTO> role = roleService.getRoleByName(roleName, realmName, TypeRole.REALM_ROLE, null);
|
||||
if (role.isEmpty()) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Rôle non trouvé"))
|
||||
.build();
|
||||
}
|
||||
|
||||
List<RoleDTO> composites = roleService.getCompositeRoles(role.get().getId(), realmName, TypeRole.REALM_ROLE, null);
|
||||
return Response.ok(composites).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des composites du rôle {}", roleName, e);
|
||||
|
||||
318
src/main/java/dev/lions/user/manager/resource/SyncResource.java
Normal file
318
src/main/java/dev/lions/user/manager/resource/SyncResource.java
Normal file
@@ -0,0 +1,318 @@
|
||||
package dev.lions.user.manager.resource;
|
||||
|
||||
import dev.lions.user.manager.dto.role.RoleDTO;
|
||||
import dev.lions.user.manager.dto.user.UserDTO;
|
||||
import dev.lions.user.manager.service.SyncService;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Content;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* REST Resource pour la synchronisation avec Keycloak
|
||||
*/
|
||||
@Path("/api/sync")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Sync", description = "Synchronisation avec Keycloak et health checks")
|
||||
@Slf4j
|
||||
public class SyncResource {
|
||||
|
||||
@Inject
|
||||
SyncService syncService;
|
||||
|
||||
@POST
|
||||
@Path("/users/{realmName}")
|
||||
@Operation(summary = "Synchroniser les utilisateurs", description = "Synchronise tous les utilisateurs depuis Keycloak")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Utilisateurs synchronisés"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "sync_manager"})
|
||||
public Response syncUsers(
|
||||
@Parameter(description = "Nom du realm") @PathParam("realmName") @NotBlank String realmName
|
||||
) {
|
||||
log.info("POST /api/sync/users/{} - Synchronisation des utilisateurs", realmName);
|
||||
|
||||
try {
|
||||
int count = syncService.syncUsersFromRealm(realmName);
|
||||
return Response.ok(new SyncUsersResponse(count, null)).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la synchronisation des utilisateurs du realm {}", realmName, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/roles/realm/{realmName}")
|
||||
@Operation(summary = "Synchroniser les rôles realm", description = "Synchronise tous les rôles realm depuis Keycloak")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Rôles realm synchronisés"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "sync_manager"})
|
||||
public Response syncRealmRoles(
|
||||
@PathParam("realmName") @NotBlank String realmName
|
||||
) {
|
||||
log.info("POST /api/sync/roles/realm/{} - Synchronisation des rôles realm", realmName);
|
||||
|
||||
try {
|
||||
int count = syncService.syncRolesFromRealm(realmName);
|
||||
return Response.ok(new SyncRolesResponse(count, null)).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la synchronisation des rôles realm du realm {}", realmName, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/roles/client/{clientId}/{realmName}")
|
||||
@Operation(summary = "Synchroniser les rôles client", description = "Synchronise tous les rôles d'un client depuis Keycloak")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Rôles client synchronisés"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "sync_manager"})
|
||||
public Response syncClientRoles(
|
||||
@PathParam("clientId") @NotBlank String clientId,
|
||||
@PathParam("realmName") @NotBlank String realmName
|
||||
) {
|
||||
log.info("POST /api/sync/roles/client/{}/{} - Synchronisation des rôles client",
|
||||
clientId, realmName);
|
||||
|
||||
try {
|
||||
// Note: syncRolesFromRealm synchronise tous les rôles realm, pas les rôles client spécifiques
|
||||
// Pour les rôles client, on synchronise tous les rôles du realm (incluant les rôles client)
|
||||
int count = syncService.syncRolesFromRealm(realmName);
|
||||
return Response.ok(new SyncRolesResponse(count, null)).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la synchronisation des rôles client du client {} (realm: {})",
|
||||
clientId, realmName, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/all/{realmName}")
|
||||
@Operation(summary = "Synchronisation complète", description = "Synchronise utilisateurs et rôles depuis Keycloak")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Synchronisation complète effectuée"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "sync_manager"})
|
||||
public Response syncAll(
|
||||
@PathParam("realmName") @NotBlank String realmName
|
||||
) {
|
||||
log.info("POST /api/sync/all/{} - Synchronisation complète", realmName);
|
||||
|
||||
try {
|
||||
Map<String, Object> result = syncService.forceSyncRealm(realmName);
|
||||
return Response.ok(result).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la synchronisation complète du realm {}", realmName, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/health")
|
||||
@Operation(summary = "Vérifier la santé de Keycloak", description = "Retourne le statut de santé de Keycloak")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Statut de santé"),
|
||||
@APIResponse(responseCode = "503", description = "Keycloak non accessible")
|
||||
})
|
||||
@RolesAllowed({"admin", "sync_manager", "auditor"})
|
||||
public Response checkHealth() {
|
||||
log.info("GET /api/sync/health - Vérification de la santé de Keycloak");
|
||||
|
||||
try {
|
||||
boolean healthy = syncService.isKeycloakAvailable();
|
||||
if (healthy) {
|
||||
return Response.ok(new HealthCheckResponse(true, "Keycloak est accessible")).build();
|
||||
} else {
|
||||
return Response.status(Response.Status.SERVICE_UNAVAILABLE)
|
||||
.entity(new HealthCheckResponse(false, "Keycloak n'est pas accessible"))
|
||||
.build();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la vérification de santé de Keycloak", e);
|
||||
return Response.status(Response.Status.SERVICE_UNAVAILABLE)
|
||||
.entity(new HealthCheckResponse(false, e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/health/detailed")
|
||||
@Operation(summary = "Statut de santé détaillé", description = "Retourne le statut de santé détaillé de Keycloak")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Statut détaillé"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "sync_manager"})
|
||||
public Response getDetailedHealthStatus() {
|
||||
log.info("GET /api/sync/health/detailed - Statut de santé détaillé");
|
||||
|
||||
try {
|
||||
Map<String, Object> status = syncService.getKeycloakHealthInfo();
|
||||
return Response.ok(status).build(); // status est maintenant une Map<String, Object>
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération du statut de santé détaillé", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/check/realm/{realmName}")
|
||||
@Operation(summary = "Vérifier l'existence d'un realm", description = "Vérifie si un realm existe")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Résultat de la vérification"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "sync_manager"})
|
||||
public Response checkRealmExists(
|
||||
@PathParam("realmName") @NotBlank String realmName
|
||||
) {
|
||||
log.info("GET /api/sync/check/realm/{} - Vérification de l'existence", realmName);
|
||||
|
||||
try {
|
||||
// Vérifier l'existence du realm en essayant de synchroniser (si ça marche, le realm existe)
|
||||
boolean exists = false;
|
||||
try {
|
||||
syncService.syncUsersFromRealm(realmName);
|
||||
exists = true;
|
||||
} catch (Exception e) {
|
||||
exists = false;
|
||||
}
|
||||
return Response.ok(new ExistsCheckResponse(exists, "realm", realmName)).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la vérification du realm {}", realmName, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/check/user/{userId}")
|
||||
@Operation(summary = "Vérifier l'existence d'un utilisateur", description = "Vérifie si un utilisateur existe")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Résultat de la vérification"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
@RolesAllowed({"admin", "sync_manager"})
|
||||
public Response checkUserExists(
|
||||
@PathParam("userId") @NotBlank String userId,
|
||||
@QueryParam("realm") @NotBlank String realmName
|
||||
) {
|
||||
log.info("GET /api/sync/check/user/{} - realm: {}", userId, realmName);
|
||||
|
||||
try {
|
||||
// Vérifier l'existence de l'utilisateur n'est plus disponible directement
|
||||
// On retourne false car cette fonctionnalité n'est plus dans l'interface
|
||||
boolean exists = false;
|
||||
return Response.ok(new ExistsCheckResponse(exists, "user", userId)).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la vérification de l'utilisateur {} dans le realm {}",
|
||||
userId, realmName, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== DTOs internes ====================
|
||||
|
||||
@Schema(description = "Réponse de synchronisation d'utilisateurs")
|
||||
public static class SyncUsersResponse {
|
||||
@Schema(description = "Nombre d'utilisateurs synchronisés")
|
||||
public int count;
|
||||
|
||||
@Schema(description = "Liste des utilisateurs synchronisés")
|
||||
public List<UserDTO> users;
|
||||
|
||||
public SyncUsersResponse(int count, List<UserDTO> users) {
|
||||
this.count = count;
|
||||
this.users = users;
|
||||
}
|
||||
}
|
||||
|
||||
@Schema(description = "Réponse de synchronisation de rôles")
|
||||
public static class SyncRolesResponse {
|
||||
@Schema(description = "Nombre de rôles synchronisés")
|
||||
public int count;
|
||||
|
||||
@Schema(description = "Liste des rôles synchronisés")
|
||||
public List<RoleDTO> roles;
|
||||
|
||||
public SyncRolesResponse(int count, List<RoleDTO> roles) {
|
||||
this.count = count;
|
||||
this.roles = roles;
|
||||
}
|
||||
}
|
||||
|
||||
@Schema(description = "Réponse de vérification de santé")
|
||||
public static class HealthCheckResponse {
|
||||
@Schema(description = "Indique si Keycloak est accessible")
|
||||
public boolean healthy;
|
||||
|
||||
@Schema(description = "Message descriptif")
|
||||
public String message;
|
||||
|
||||
public HealthCheckResponse(boolean healthy, String message) {
|
||||
this.healthy = healthy;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
@Schema(description = "Réponse de vérification d'existence")
|
||||
public static class ExistsCheckResponse {
|
||||
@Schema(description = "Indique si la ressource existe")
|
||||
public boolean exists;
|
||||
|
||||
@Schema(description = "Type de ressource (realm, user, client, etc.)")
|
||||
public String resourceType;
|
||||
|
||||
@Schema(description = "Identifiant de la ressource")
|
||||
public String resourceId;
|
||||
|
||||
public ExistsCheckResponse(boolean exists, String resourceType, String resourceId) {
|
||||
this.exists = exists;
|
||||
this.resourceType = resourceType;
|
||||
this.resourceId = resourceId;
|
||||
}
|
||||
}
|
||||
|
||||
@Schema(description = "Réponse d'erreur")
|
||||
public static class ErrorResponse {
|
||||
@Schema(description = "Message d'erreur")
|
||||
public String message;
|
||||
|
||||
public ErrorResponse(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package dev.lions.user.manager.security;
|
||||
|
||||
import jakarta.annotation.Priority;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.Priorities;
|
||||
import jakarta.ws.rs.container.ContainerRequestContext;
|
||||
import jakarta.ws.rs.container.ContainerRequestFilter;
|
||||
import jakarta.ws.rs.core.SecurityContext;
|
||||
import jakarta.ws.rs.ext.Provider;
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.security.Principal;
|
||||
|
||||
/**
|
||||
* Filtre JAX-RS pour remplacer le SecurityContext en mode développement
|
||||
* En dev, remplace le SecurityContext par un mock qui autorise tous les rôles
|
||||
* En prod, laisse le SecurityContext réel de Quarkus
|
||||
*/
|
||||
@Provider
|
||||
@Priority(Priorities.AUTHENTICATION - 10) // S'exécute très tôt, avant l'authentification
|
||||
public class DevSecurityContextProducer implements ContainerRequestFilter {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(DevSecurityContextProducer.class);
|
||||
|
||||
@Inject
|
||||
@ConfigProperty(name = "quarkus.profile", defaultValue = "prod")
|
||||
String profile;
|
||||
|
||||
@Inject
|
||||
@ConfigProperty(name = "quarkus.oidc.enabled", defaultValue = "true")
|
||||
boolean oidcEnabled;
|
||||
|
||||
@Override
|
||||
public void filter(ContainerRequestContext requestContext) {
|
||||
// Détecter le mode dev : si OIDC est désactivé, on est probablement en dev
|
||||
// ou si le profil est explicitement "dev" ou "development"
|
||||
boolean isDevMode = !oidcEnabled || "dev".equals(profile) || "development".equals(profile);
|
||||
|
||||
if (isDevMode) {
|
||||
String path = requestContext.getUriInfo().getPath();
|
||||
LOG.infof("Mode dev détecté (profile=%s, oidc.enabled=%s): remplacement du SecurityContext pour le chemin %s",
|
||||
profile, oidcEnabled, path);
|
||||
SecurityContext original = requestContext.getSecurityContext();
|
||||
requestContext.setSecurityContext(new DevSecurityContext(original));
|
||||
LOG.debugf("SecurityContext remplacé - isUserInRole('admin')=%s, isUserInRole('user_manager')=%s",
|
||||
new DevSecurityContext(original).isUserInRole("admin"),
|
||||
new DevSecurityContext(original).isUserInRole("user_manager"));
|
||||
} else {
|
||||
LOG.debugf("Mode prod - SecurityContext original conservé (profile=%s, oidc.enabled=%s)", profile, oidcEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SecurityContext mock pour le mode développement
|
||||
* Simule un utilisateur avec tous les rôles nécessaires
|
||||
*/
|
||||
private static class DevSecurityContext implements SecurityContext {
|
||||
|
||||
private final SecurityContext original;
|
||||
private final Principal principal = new Principal() {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "dev-user";
|
||||
}
|
||||
};
|
||||
|
||||
public DevSecurityContext(SecurityContext original) {
|
||||
this.original = original;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Principal getUserPrincipal() {
|
||||
return principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserInRole(String role) {
|
||||
// En dev, autoriser tous les rôles
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecure() {
|
||||
return original != null ? original.isSecure() : false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthenticationScheme() {
|
||||
return "DEV";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,9 +61,9 @@ public class AuditServiceImpl implements AuditService {
|
||||
auditLog.getTypeAction(),
|
||||
auditLog.getActeurUsername(),
|
||||
auditLog.getRessourceType() + ":" + auditLog.getRessourceId(),
|
||||
auditLog.isSucces(),
|
||||
auditLog.getAdresseIp(),
|
||||
auditLog.getDetails());
|
||||
auditLog.isSuccessful(),
|
||||
auditLog.getIpAddress(),
|
||||
auditLog.getDescription());
|
||||
|
||||
// Stocker en mémoire
|
||||
auditLogs.put(auditLog.getId(), auditLog);
|
||||
@@ -79,17 +79,21 @@ public class AuditServiceImpl implements AuditService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logSuccess(@NotBlank String acteurUsername, @NotNull TypeActionAudit typeAction,
|
||||
@NotBlank String ressourceType, @NotBlank String ressourceId,
|
||||
String adresseIp, String details) {
|
||||
public void logSuccess(@NotNull TypeActionAudit typeAction,
|
||||
@NotBlank String ressourceType,
|
||||
String ressourceId,
|
||||
String ressourceName,
|
||||
@NotBlank String realmName,
|
||||
@NotBlank String acteurUserId,
|
||||
String description) {
|
||||
AuditLogDTO auditLog = AuditLogDTO.builder()
|
||||
.acteurUsername(acteurUsername)
|
||||
.acteurUserId(acteurUserId)
|
||||
.acteurUsername(acteurUserId) // Utiliser acteurUserId comme username pour l'instant
|
||||
.typeAction(typeAction)
|
||||
.ressourceType(ressourceType)
|
||||
.ressourceId(ressourceId)
|
||||
.succes(true)
|
||||
.adresseIp(adresseIp)
|
||||
.details(details)
|
||||
.ressourceId(ressourceId != null ? ressourceId : "")
|
||||
.success(true)
|
||||
.description(description)
|
||||
.dateAction(LocalDateTime.now())
|
||||
.build();
|
||||
|
||||
@@ -97,17 +101,22 @@ public class AuditServiceImpl implements AuditService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logFailure(@NotBlank String acteurUsername, @NotNull TypeActionAudit typeAction,
|
||||
@NotBlank String ressourceType, @NotBlank String ressourceId,
|
||||
String adresseIp, @NotBlank String messageErreur) {
|
||||
public void logFailure(@NotNull TypeActionAudit typeAction,
|
||||
@NotBlank String ressourceType,
|
||||
String ressourceId,
|
||||
String ressourceName,
|
||||
@NotBlank String realmName,
|
||||
@NotBlank String acteurUserId,
|
||||
String errorCode,
|
||||
String errorMessage) {
|
||||
AuditLogDTO auditLog = AuditLogDTO.builder()
|
||||
.acteurUsername(acteurUsername)
|
||||
.acteurUserId(acteurUserId)
|
||||
.acteurUsername(acteurUserId) // Utiliser acteurUserId comme username pour l'instant
|
||||
.typeAction(typeAction)
|
||||
.ressourceType(ressourceType)
|
||||
.ressourceId(ressourceId)
|
||||
.succes(false)
|
||||
.adresseIp(adresseIp)
|
||||
.messageErreur(messageErreur)
|
||||
.ressourceId(ressourceId != null ? ressourceId : "")
|
||||
.success(false)
|
||||
.errorMessage(errorMessage)
|
||||
.dateAction(LocalDateTime.now())
|
||||
.build();
|
||||
|
||||
@@ -115,7 +124,155 @@ public class AuditServiceImpl implements AuditService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AuditLogDTO> searchLogs(@NotBlank String acteurUsername, LocalDateTime dateDebut,
|
||||
public List<AuditLogDTO> findByActeur(@NotBlank String acteurUserId,
|
||||
LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin,
|
||||
int page,
|
||||
int pageSize) {
|
||||
return searchLogs(acteurUserId, dateDebut, dateFin, null, null, null, page, pageSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AuditLogDTO> findByRessource(@NotBlank String ressourceType,
|
||||
@NotBlank String ressourceId,
|
||||
LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin,
|
||||
int page,
|
||||
int pageSize) {
|
||||
return searchLogs(null, dateDebut, dateFin, null, ressourceType, null, page, pageSize)
|
||||
.stream()
|
||||
.filter(log -> ressourceId.equals(log.getRessourceId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AuditLogDTO> findByTypeAction(@NotNull TypeActionAudit typeAction,
|
||||
@NotBlank String realmName,
|
||||
LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin,
|
||||
int page,
|
||||
int pageSize) {
|
||||
return searchLogs(null, dateDebut, dateFin, typeAction, null, null, page, pageSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AuditLogDTO> findByRealm(@NotBlank String realmName,
|
||||
LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin,
|
||||
int page,
|
||||
int pageSize) {
|
||||
// Pour l'instant, on retourne tous les logs car on n'a pas de champ realmName dans AuditLogDTO
|
||||
return searchLogs(null, dateDebut, dateFin, null, null, null, page, pageSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AuditLogDTO> findFailures(@NotBlank String realmName,
|
||||
LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin,
|
||||
int page,
|
||||
int pageSize) {
|
||||
return searchLogs(null, dateDebut, dateFin, null, null, false, page, pageSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AuditLogDTO> findCriticalActions(@NotBlank String realmName,
|
||||
LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin,
|
||||
int page,
|
||||
int pageSize) {
|
||||
// Les actions critiques sont USER_DELETE, ROLE_DELETE, etc.
|
||||
return auditLogs.values().stream()
|
||||
.filter(log -> {
|
||||
TypeActionAudit type = log.getTypeAction();
|
||||
return type == TypeActionAudit.USER_DELETE ||
|
||||
type == TypeActionAudit.ROLE_DELETE ||
|
||||
type == TypeActionAudit.SESSION_REVOKE_ALL;
|
||||
})
|
||||
.filter(log -> {
|
||||
if (dateDebut != null && log.getDateAction().isBefore(dateDebut)) {
|
||||
return false;
|
||||
}
|
||||
if (dateFin != null && log.getDateAction().isAfter(dateFin)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.sorted((a, b) -> b.getDateAction().compareTo(a.getDateAction()))
|
||||
.skip((long) page * pageSize)
|
||||
.limit(pageSize)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<TypeActionAudit, Long> countByActionType(@NotBlank String realmName,
|
||||
LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin) {
|
||||
return getActionStatistics(dateDebut, dateFin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Long> countByActeur(@NotBlank String realmName,
|
||||
LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin) {
|
||||
return getUserActivityStatistics(dateDebut, dateFin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Long> countSuccessVsFailure(@NotBlank String realmName,
|
||||
LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin) {
|
||||
long successCount = getSuccessCount(dateDebut, dateFin);
|
||||
long failureCount = getFailureCount(dateDebut, dateFin);
|
||||
|
||||
Map<String, Long> result = new java.util.HashMap<>();
|
||||
result.put("success", successCount);
|
||||
result.put("failure", failureCount);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String exportToCSV(@NotBlank String realmName,
|
||||
LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin) {
|
||||
List<String> csvLines = exportLogsToCSV(dateDebut, dateFin);
|
||||
return String.join("\n", csvLines);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long purgeOldLogs(@NotNull LocalDateTime dateLimite) {
|
||||
long beforeCount = auditLogs.size();
|
||||
auditLogs.entrySet().removeIf(entry ->
|
||||
entry.getValue().getDateAction().isBefore(dateLimite)
|
||||
);
|
||||
long afterCount = auditLogs.size();
|
||||
return beforeCount - afterCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getAuditStatistics(@NotBlank String realmName,
|
||||
LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin) {
|
||||
Map<String, Object> stats = new java.util.HashMap<>();
|
||||
stats.put("total", auditLogs.values().stream()
|
||||
.filter(log -> {
|
||||
if (dateDebut != null && log.getDateAction().isBefore(dateDebut)) {
|
||||
return false;
|
||||
}
|
||||
if (dateFin != null && log.getDateAction().isAfter(dateFin)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.count());
|
||||
stats.put("success", getSuccessCount(dateDebut, dateFin));
|
||||
stats.put("failure", getFailureCount(dateDebut, dateFin));
|
||||
stats.put("byActionType", countByActionType(realmName, dateDebut, dateFin));
|
||||
stats.put("byActeur", countByActeur(realmName, dateDebut, dateFin));
|
||||
return stats;
|
||||
}
|
||||
|
||||
// Méthode privée helper pour la recherche
|
||||
private List<AuditLogDTO> searchLogs(String acteurUsername, LocalDateTime dateDebut,
|
||||
LocalDateTime dateFin, TypeActionAudit typeAction,
|
||||
String ressourceType, Boolean succes,
|
||||
int page, int pageSize) {
|
||||
@@ -151,7 +308,7 @@ public class AuditServiceImpl implements AuditService {
|
||||
}
|
||||
|
||||
// Filtre par succès/échec
|
||||
if (succes != null && succes != log.isSucces()) {
|
||||
if (succes != null && succes != log.isSuccessful()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -163,58 +320,8 @@ public class AuditServiceImpl implements AuditService {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AuditLogDTO> getLogsByActeur(@NotBlank String acteurUsername, int limit) {
|
||||
log.debug("Récupération des {} derniers logs de l'acteur: {}", limit, acteurUsername);
|
||||
|
||||
return auditLogs.values().stream()
|
||||
.filter(log -> acteurUsername.equals(log.getActeurUsername()))
|
||||
.sorted((a, b) -> b.getDateAction().compareTo(a.getDateAction()))
|
||||
.limit(limit)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AuditLogDTO> getLogsByRessource(@NotBlank String ressourceType,
|
||||
@NotBlank String ressourceId, int limit) {
|
||||
log.debug("Récupération des {} derniers logs de la ressource: {}:{}",
|
||||
limit, ressourceType, ressourceId);
|
||||
|
||||
return auditLogs.values().stream()
|
||||
.filter(log -> ressourceType.equals(log.getRessourceType()) &&
|
||||
ressourceId.equals(log.getRessourceId()))
|
||||
.sorted((a, b) -> b.getDateAction().compareTo(a.getDateAction()))
|
||||
.limit(limit)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AuditLogDTO> getLogsByAction(@NotNull TypeActionAudit typeAction,
|
||||
LocalDateTime dateDebut, LocalDateTime dateFin,
|
||||
int limit) {
|
||||
log.debug("Récupération des {} logs de type: {} entre {} et {}",
|
||||
limit, typeAction, dateDebut, dateFin);
|
||||
|
||||
return auditLogs.values().stream()
|
||||
.filter(log -> {
|
||||
if (!typeAction.equals(log.getTypeAction())) {
|
||||
return false;
|
||||
}
|
||||
if (dateDebut != null && log.getDateAction().isBefore(dateDebut)) {
|
||||
return false;
|
||||
}
|
||||
if (dateFin != null && log.getDateAction().isAfter(dateFin)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.sorted((a, b) -> b.getDateAction().compareTo(a.getDateAction()))
|
||||
.limit(limit)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<TypeActionAudit, Long> getActionStatistics(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
||||
// Méthodes privées helper
|
||||
private Map<TypeActionAudit, Long> getActionStatistics(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
||||
log.debug("Calcul des statistiques d'actions entre {} et {}", dateDebut, dateFin);
|
||||
|
||||
return auditLogs.values().stream()
|
||||
@@ -233,8 +340,7 @@ public class AuditServiceImpl implements AuditService {
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Long> getUserActivityStatistics(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
||||
private Map<String, Long> getUserActivityStatistics(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
||||
log.debug("Calcul des statistiques d'activité utilisateurs entre {} et {}", dateDebut, dateFin);
|
||||
|
||||
return auditLogs.values().stream()
|
||||
@@ -253,13 +359,12 @@ public class AuditServiceImpl implements AuditService {
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFailureCount(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
||||
private long getFailureCount(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
||||
log.debug("Comptage des échecs entre {} et {}", dateDebut, dateFin);
|
||||
|
||||
return auditLogs.values().stream()
|
||||
.filter(log -> {
|
||||
if (log.isSucces()) {
|
||||
if (log.isSuccessful()) {
|
||||
return false; // On ne compte que les échecs
|
||||
}
|
||||
if (dateDebut != null && log.getDateAction().isBefore(dateDebut)) {
|
||||
@@ -273,13 +378,12 @@ public class AuditServiceImpl implements AuditService {
|
||||
.count();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSuccessCount(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
||||
private long getSuccessCount(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
||||
log.debug("Comptage des succès entre {} et {}", dateDebut, dateFin);
|
||||
|
||||
return auditLogs.values().stream()
|
||||
.filter(log -> {
|
||||
if (!log.isSucces()) {
|
||||
if (!log.isSuccessful()) {
|
||||
return false; // On ne compte que les succès
|
||||
}
|
||||
if (dateDebut != null && log.getDateAction().isBefore(dateDebut)) {
|
||||
@@ -293,8 +397,7 @@ public class AuditServiceImpl implements AuditService {
|
||||
.count();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> exportLogsToCSV(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
||||
private List<String> exportLogsToCSV(LocalDateTime dateDebut, LocalDateTime dateFin) {
|
||||
log.info("Export CSV des logs d'audit entre {} et {}", dateDebut, dateFin);
|
||||
|
||||
List<String> csvLines = new ArrayList<>();
|
||||
@@ -322,10 +425,10 @@ public class AuditServiceImpl implements AuditService {
|
||||
log.getTypeAction(),
|
||||
log.getRessourceType(),
|
||||
log.getRessourceId(),
|
||||
log.isSucces(),
|
||||
log.getAdresseIp() != null ? log.getAdresseIp() : "",
|
||||
log.getDetails() != null ? log.getDetails().replace("\"", "\"\"") : "",
|
||||
log.getMessageErreur() != null ? log.getMessageErreur().replace("\"", "\"\"") : ""
|
||||
log.isSuccessful(),
|
||||
log.getIpAddress() != null ? log.getIpAddress() : "",
|
||||
log.getDescription() != null ? log.getDescription().replace("\"", "\"\"") : "",
|
||||
log.getErrorMessage() != null ? log.getErrorMessage().replace("\"", "\"\"") : ""
|
||||
);
|
||||
csvLines.add(csvLine);
|
||||
});
|
||||
@@ -334,24 +437,6 @@ public class AuditServiceImpl implements AuditService {
|
||||
return csvLines;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void purgeOldLogs(int joursDAnc ienneté) {
|
||||
log.info("Purge des logs d'audit de plus de {} jours", joursDAncienneté);
|
||||
|
||||
LocalDateTime dateLimit = LocalDateTime.now().minusDays(joursDAncienneté);
|
||||
|
||||
long beforeCount = auditLogs.size();
|
||||
auditLogs.entrySet().removeIf(entry ->
|
||||
entry.getValue().getDateAction().isBefore(dateLimit)
|
||||
);
|
||||
long afterCount = auditLogs.size();
|
||||
|
||||
log.info("Purge terminée: {} logs supprimés", beforeCount - afterCount);
|
||||
|
||||
// TODO: Si base de données utilisée, exécuter:
|
||||
// DELETE FROM audit_log WHERE date_action < :dateLimit
|
||||
}
|
||||
|
||||
// ==================== Méthodes utilitaires ====================
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,8 @@ import dev.lions.user.manager.dto.role.RoleDTO;
|
||||
import dev.lions.user.manager.enums.role.TypeRole;
|
||||
import dev.lions.user.manager.mapper.RoleMapper;
|
||||
import dev.lions.user.manager.service.RoleService;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.Valid;
|
||||
@@ -35,7 +37,7 @@ public class RoleServiceImpl implements RoleService {
|
||||
|
||||
@Override
|
||||
public RoleDTO createRealmRole(@Valid @NotNull RoleDTO roleDTO, @NotBlank String realmName) {
|
||||
log.info("Création du rôle realm: {} dans le realm: {}", roleDTO.getNom(), realmName);
|
||||
log.info("Création du rôle realm: {} dans le realm: {}", roleDTO.getName(), realmName);
|
||||
|
||||
RolesResource rolesResource = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
@@ -43,8 +45,8 @@ public class RoleServiceImpl implements RoleService {
|
||||
|
||||
// Vérifier si le rôle existe déjà
|
||||
try {
|
||||
rolesResource.get(roleDTO.getNom()).toRepresentation();
|
||||
throw new IllegalArgumentException("Le rôle " + roleDTO.getNom() + " existe déjà");
|
||||
rolesResource.get(roleDTO.getName()).toRepresentation();
|
||||
throw new IllegalArgumentException("Le rôle " + roleDTO.getName() + " existe déjà");
|
||||
} catch (NotFoundException e) {
|
||||
// OK, le rôle n'existe pas
|
||||
}
|
||||
@@ -53,12 +55,12 @@ public class RoleServiceImpl implements RoleService {
|
||||
rolesResource.create(roleRep);
|
||||
|
||||
// Récupérer le rôle créé avec son ID
|
||||
RoleRepresentation createdRole = rolesResource.get(roleDTO.getNom()).toRepresentation();
|
||||
RoleRepresentation createdRole = rolesResource.get(roleDTO.getName()).toRepresentation();
|
||||
return RoleMapper.toDTO(createdRole, realmName, TypeRole.REALM_ROLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<RoleDTO> getRealmRoleById(@NotBlank String roleId, @NotBlank String realmName) {
|
||||
// Méthodes privées helper pour utilisation interne
|
||||
private Optional<RoleDTO> getRealmRoleById(String roleId, String realmName) {
|
||||
log.debug("Récupération du rôle realm par ID: {} dans le realm: {}", roleId, realmName);
|
||||
|
||||
try {
|
||||
@@ -78,8 +80,7 @@ public class RoleServiceImpl implements RoleService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public 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);
|
||||
|
||||
try {
|
||||
@@ -97,68 +98,164 @@ public class RoleServiceImpl implements RoleService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleDTO updateRealmRole(@NotBlank String roleName, @Valid @NotNull RoleDTO roleDTO,
|
||||
@NotBlank String realmName) {
|
||||
log.info("Mise à jour du rôle realm: {} dans le realm: {}", roleName, realmName);
|
||||
public RoleDTO updateRole(@NotBlank String roleId,
|
||||
@Valid @NotNull RoleDTO role,
|
||||
@NotBlank String realmName,
|
||||
@NotNull TypeRole typeRole,
|
||||
String clientName) {
|
||||
log.info("Mise à jour du rôle {} (type: {}) dans le realm: {}", roleId, typeRole, realmName);
|
||||
|
||||
RoleResource roleResource = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.roles()
|
||||
.get(roleName);
|
||||
if (typeRole == TypeRole.REALM_ROLE) {
|
||||
// Trouver le nom du rôle par son ID
|
||||
Optional<RoleDTO> existingRole = getRealmRoleById(roleId, realmName);
|
||||
if (existingRole.isEmpty()) {
|
||||
throw new jakarta.ws.rs.NotFoundException("Rôle non trouvé: " + roleId);
|
||||
}
|
||||
String roleName = existingRole.get().getName();
|
||||
|
||||
RoleRepresentation roleRep = roleResource.toRepresentation();
|
||||
RoleResource roleResource = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.roles()
|
||||
.get(roleName);
|
||||
|
||||
// Mettre à jour uniquement les champs modifiables
|
||||
if (roleDTO.getDescription() != null) {
|
||||
roleRep.setDescription(roleDTO.getDescription());
|
||||
RoleRepresentation roleRep = roleResource.toRepresentation();
|
||||
|
||||
// Mettre à jour uniquement les champs modifiables
|
||||
if (role.getDescription() != null) {
|
||||
roleRep.setDescription(role.getDescription());
|
||||
}
|
||||
|
||||
roleResource.update(roleRep);
|
||||
|
||||
// Retourner le rôle mis à jour
|
||||
return RoleMapper.toDTO(roleResource.toRepresentation(), realmName, TypeRole.REALM_ROLE);
|
||||
} else if (typeRole == TypeRole.CLIENT_ROLE && clientName != null) {
|
||||
// Pour les rôles client, trouver le nom par ID puis mettre à jour
|
||||
Optional<RoleDTO> existingRole = getRoleById(roleId, realmName, typeRole, clientName);
|
||||
if (existingRole.isEmpty()) {
|
||||
throw new jakarta.ws.rs.NotFoundException("Rôle non trouvé: " + roleId);
|
||||
}
|
||||
String roleName = existingRole.get().getName();
|
||||
|
||||
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.clients();
|
||||
|
||||
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
||||
clientsResource.findByClientId(clientName);
|
||||
|
||||
if (clients.isEmpty()) {
|
||||
throw new IllegalArgumentException("Client " + clientName + " non trouvé");
|
||||
}
|
||||
|
||||
String internalClientId = clients.get(0).getId();
|
||||
RoleResource roleResource = clientsResource.get(internalClientId)
|
||||
.roles()
|
||||
.get(roleName);
|
||||
|
||||
RoleRepresentation roleRep = roleResource.toRepresentation();
|
||||
|
||||
if (role.getDescription() != null) {
|
||||
roleRep.setDescription(role.getDescription());
|
||||
}
|
||||
|
||||
roleResource.update(roleRep);
|
||||
|
||||
RoleDTO result = RoleMapper.toDTO(roleResource.toRepresentation(), realmName, TypeRole.CLIENT_ROLE);
|
||||
result.setClientId(clientName);
|
||||
return result;
|
||||
}
|
||||
|
||||
roleResource.update(roleRep);
|
||||
|
||||
// Retourner le rôle mis à jour
|
||||
return RoleMapper.toDTO(roleResource.toRepresentation(), realmName, TypeRole.REALM_ROLE);
|
||||
throw new IllegalArgumentException("Type de rôle non supporté pour la mise à jour: " + typeRole);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteRealmRole(@NotBlank String roleName, @NotBlank String realmName) {
|
||||
log.info("Suppression du rôle realm: {} dans le realm: {}", roleName, realmName);
|
||||
public void deleteRole(@NotBlank String roleId,
|
||||
@NotBlank String realmName,
|
||||
@NotNull TypeRole typeRole,
|
||||
String clientName) {
|
||||
log.info("Suppression du rôle {} (type: {}) dans le realm: {}", roleId, typeRole, realmName);
|
||||
|
||||
keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.roles()
|
||||
.deleteRole(roleName);
|
||||
if (typeRole == TypeRole.REALM_ROLE) {
|
||||
// Trouver le nom du rôle par son ID
|
||||
Optional<RoleDTO> existingRole = getRealmRoleById(roleId, realmName);
|
||||
if (existingRole.isEmpty()) {
|
||||
throw new jakarta.ws.rs.NotFoundException("Rôle non trouvé: " + roleId);
|
||||
}
|
||||
String roleName = existingRole.get().getName();
|
||||
|
||||
keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.roles()
|
||||
.deleteRole(roleName);
|
||||
} else if (typeRole == TypeRole.CLIENT_ROLE && clientName != null) {
|
||||
// Trouver le nom du rôle par son ID
|
||||
Optional<RoleDTO> existingRole = getRoleById(roleId, realmName, typeRole, clientName);
|
||||
if (existingRole.isEmpty()) {
|
||||
throw new jakarta.ws.rs.NotFoundException("Rôle non trouvé: " + roleId);
|
||||
}
|
||||
String roleName = existingRole.get().getName();
|
||||
|
||||
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.clients();
|
||||
|
||||
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
||||
clientsResource.findByClientId(clientName);
|
||||
|
||||
if (clients.isEmpty()) {
|
||||
throw new IllegalArgumentException("Client " + clientName + " non trouvé");
|
||||
}
|
||||
|
||||
String internalClientId = clients.get(0).getId();
|
||||
clientsResource.get(internalClientId).roles().deleteRole(roleName);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Type de rôle non supporté pour la suppression: " + typeRole);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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()
|
||||
.realm(realmName)
|
||||
.roles()
|
||||
.list();
|
||||
try {
|
||||
// Vérifier que le realm existe
|
||||
if (!keycloakAdminClient.realmExists(realmName)) {
|
||||
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 ====================
|
||||
|
||||
@Override
|
||||
public RoleDTO createClientRole(@Valid @NotNull RoleDTO roleDTO, @NotBlank String clientId,
|
||||
@NotBlank String realmName) {
|
||||
public RoleDTO createClientRole(@Valid @NotNull RoleDTO roleDTO, @NotBlank String realmName,
|
||||
@NotBlank String clientName) {
|
||||
log.info("Création du rôle client: {} pour le client: {} dans le realm: {}",
|
||||
roleDTO.getNom(), clientId, realmName);
|
||||
roleDTO.getName(), clientName, realmName);
|
||||
|
||||
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.clients();
|
||||
|
||||
// Trouver le client par clientId
|
||||
// Trouver le client par clientId (on utilise clientName comme clientId)
|
||||
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
||||
clientsResource.findByClientId(clientId);
|
||||
clientsResource.findByClientId(clientName);
|
||||
|
||||
if (clients.isEmpty()) {
|
||||
throw new IllegalArgumentException("Client " + clientId + " non trouvé");
|
||||
throw new IllegalArgumentException("Client " + clientName + " non trouvé");
|
||||
}
|
||||
|
||||
String internalClientId = clients.get(0).getId();
|
||||
@@ -166,8 +263,8 @@ public class RoleServiceImpl implements RoleService {
|
||||
|
||||
// Vérifier si le rôle existe déjà
|
||||
try {
|
||||
rolesResource.get(roleDTO.getNom()).toRepresentation();
|
||||
throw new IllegalArgumentException("Le rôle " + roleDTO.getNom() + " existe déjà pour ce client");
|
||||
rolesResource.get(roleDTO.getName()).toRepresentation();
|
||||
throw new IllegalArgumentException("Le rôle " + roleDTO.getName() + " existe déjà pour ce client");
|
||||
} catch (NotFoundException e) {
|
||||
// OK, le rôle n'existe pas
|
||||
}
|
||||
@@ -176,16 +273,16 @@ public class RoleServiceImpl implements RoleService {
|
||||
rolesResource.create(roleRep);
|
||||
|
||||
// Récupérer le rôle créé
|
||||
RoleRepresentation createdRole = rolesResource.get(roleDTO.getNom()).toRepresentation();
|
||||
RoleRepresentation createdRole = rolesResource.get(roleDTO.getName()).toRepresentation();
|
||||
RoleDTO result = RoleMapper.toDTO(createdRole, realmName, TypeRole.CLIENT_ROLE);
|
||||
result.setClientId(clientId);
|
||||
result.setClientId(clientName);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<RoleDTO> getClientRoleByName(@NotBlank String roleName, @NotBlank String clientId,
|
||||
@NotBlank String realmName) {
|
||||
// Méthode privée helper pour utilisation interne
|
||||
private Optional<RoleDTO> getClientRoleByName(String roleName, String clientId,
|
||||
String realmName) {
|
||||
log.debug("Récupération du rôle client: {} pour le client: {} dans le realm: {}",
|
||||
roleName, clientId, realmName);
|
||||
|
||||
@@ -218,37 +315,17 @@ public class RoleServiceImpl implements RoleService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void deleteClientRole(@NotBlank String roleName, @NotBlank String clientId,
|
||||
@NotBlank String realmName) {
|
||||
log.info("Suppression du rôle client: {} pour le client: {} dans le realm: {}",
|
||||
roleName, clientId, realmName);
|
||||
public List<RoleDTO> getAllClientRoles(@NotBlank String realmName, @NotBlank String clientName) {
|
||||
log.debug("Récupération de tous les rôles du client: {} dans le realm: {}", clientName, realmName);
|
||||
|
||||
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.clients();
|
||||
|
||||
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
||||
clientsResource.findByClientId(clientId);
|
||||
|
||||
if (clients.isEmpty()) {
|
||||
throw new IllegalArgumentException("Client " + clientId + " non trouvé");
|
||||
}
|
||||
|
||||
String internalClientId = clients.get(0).getId();
|
||||
clientsResource.get(internalClientId).roles().deleteRole(roleName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RoleDTO> getAllClientRoles(@NotBlank String clientId, @NotBlank String realmName) {
|
||||
log.debug("Récupération de tous les rôles du client: {} dans le realm: {}", clientId, realmName);
|
||||
|
||||
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.clients();
|
||||
|
||||
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
||||
clientsResource.findByClientId(clientId);
|
||||
clientsResource.findByClientId(clientName);
|
||||
|
||||
if (clients.isEmpty()) {
|
||||
return List.of();
|
||||
@@ -260,16 +337,102 @@ public class RoleServiceImpl implements RoleService {
|
||||
.list();
|
||||
|
||||
List<RoleDTO> roles = RoleMapper.toDTOList(roleReps, realmName, TypeRole.CLIENT_ROLE);
|
||||
roles.forEach(role -> role.setClientId(clientId));
|
||||
roles.forEach(role -> role.setClientId(clientName));
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<RoleDTO> getRoleById(@NotBlank String roleId,
|
||||
@NotBlank String realmName,
|
||||
@NotNull TypeRole typeRole,
|
||||
String clientName) {
|
||||
log.debug("Récupération du rôle par ID: {} (type: {}) dans le realm: {}", roleId, typeRole, realmName);
|
||||
|
||||
if (typeRole == TypeRole.REALM_ROLE) {
|
||||
return getRealmRoleById(roleId, realmName);
|
||||
} else if (typeRole == TypeRole.CLIENT_ROLE && clientName != null) {
|
||||
// Pour les rôles client, on doit lister tous les rôles du client et trouver par ID
|
||||
try {
|
||||
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.clients();
|
||||
|
||||
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
||||
clientsResource.findByClientId(clientName);
|
||||
|
||||
if (clients.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
String internalClientId = clients.get(0).getId();
|
||||
List<RoleRepresentation> roles = clientsResource.get(internalClientId)
|
||||
.roles()
|
||||
.list();
|
||||
|
||||
return roles.stream()
|
||||
.filter(r -> r.getId().equals(roleId))
|
||||
.findFirst()
|
||||
.map(r -> {
|
||||
RoleDTO roleDTO = RoleMapper.toDTO(r, realmName, TypeRole.CLIENT_ROLE);
|
||||
roleDTO.setClientId(clientName);
|
||||
return roleDTO;
|
||||
});
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération du rôle client {}", roleId, e);
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<RoleDTO> getRoleByName(@NotBlank String roleName,
|
||||
@NotBlank String realmName,
|
||||
@NotNull TypeRole typeRole,
|
||||
String clientName) {
|
||||
log.debug("Récupération du rôle par nom: {} (type: {}) dans le realm: {}", roleName, typeRole, realmName);
|
||||
|
||||
if (typeRole == TypeRole.REALM_ROLE) {
|
||||
return getRealmRoleByName(roleName, realmName);
|
||||
} else if (typeRole == TypeRole.CLIENT_ROLE && clientName != null) {
|
||||
return getClientRoleByName(roleName, clientName, realmName);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
// ==================== Attribution de rôles ====================
|
||||
|
||||
@Override
|
||||
public void assignRealmRolesToUser(@NotBlank String userId, @NotNull List<String> roleNames,
|
||||
@NotBlank String realmName) {
|
||||
public void assignRolesToUser(@Valid @NotNull RoleAssignmentDTO assignment) {
|
||||
log.info("Attribution de {} rôles {} à l'utilisateur {} dans le realm {}",
|
||||
assignment.getRoleNames().size(), assignment.getTypeRole(), assignment.getUserId(), assignment.getRealmName());
|
||||
|
||||
if (assignment.getTypeRole() == TypeRole.REALM_ROLE) {
|
||||
assignRealmRolesToUser(assignment.getUserId(), assignment.getRoleNames(), assignment.getRealmName());
|
||||
} else if (assignment.getTypeRole() == TypeRole.CLIENT_ROLE && assignment.getClientName() != null) {
|
||||
assignClientRolesToUser(assignment.getUserId(), assignment.getClientName(), assignment.getRoleNames(), assignment.getRealmName());
|
||||
} else {
|
||||
throw new IllegalArgumentException("Données d'attribution invalides pour le type de rôle: " + assignment.getTypeRole());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revokeRolesFromUser(@Valid @NotNull RoleAssignmentDTO assignment) {
|
||||
log.info("Révocation de {} rôles {} pour l'utilisateur {} dans le realm {}",
|
||||
assignment.getRoleNames().size(), assignment.getTypeRole(), assignment.getUserId(), assignment.getRealmName());
|
||||
|
||||
if (assignment.getTypeRole() == TypeRole.REALM_ROLE) {
|
||||
revokeRealmRolesFromUser(assignment.getUserId(), assignment.getRoleNames(), assignment.getRealmName());
|
||||
} else if (assignment.getTypeRole() == TypeRole.CLIENT_ROLE && assignment.getClientName() != null) {
|
||||
revokeClientRolesFromUser(assignment.getUserId(), assignment.getClientName(), assignment.getRoleNames(), assignment.getRealmName());
|
||||
} else {
|
||||
throw new IllegalArgumentException("Données de révocation invalides pour le type de rôle: " + assignment.getTypeRole());
|
||||
}
|
||||
}
|
||||
|
||||
private void assignRealmRolesToUser(String userId, List<String> roleNames,
|
||||
String realmName) {
|
||||
log.info("Attribution de {} rôles realm à l'utilisateur {} dans le realm {}",
|
||||
roleNames.size(), userId, realmName);
|
||||
|
||||
@@ -299,9 +462,8 @@ public class RoleServiceImpl implements RoleService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revokeRealmRolesFromUser(@NotBlank String userId, @NotNull List<String> roleNames,
|
||||
@NotBlank String realmName) {
|
||||
private void revokeRealmRolesFromUser(String userId, List<String> roleNames,
|
||||
String realmName) {
|
||||
log.info("Révocation de {} rôles realm pour l'utilisateur {} dans le realm {}",
|
||||
roleNames.size(), userId, realmName);
|
||||
|
||||
@@ -331,9 +493,8 @@ public class RoleServiceImpl implements RoleService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assignClientRolesToUser(@NotBlank String userId, @NotBlank String clientId,
|
||||
@NotNull List<String> roleNames, @NotBlank String realmName) {
|
||||
private void assignClientRolesToUser(String userId, String clientId,
|
||||
List<String> roleNames, String realmName) {
|
||||
log.info("Attribution de {} rôles du client {} à l'utilisateur {} dans le realm {}",
|
||||
roleNames.size(), clientId, userId, realmName);
|
||||
|
||||
@@ -373,9 +534,8 @@ public class RoleServiceImpl implements RoleService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revokeClientRolesFromUser(@NotBlank String userId, @NotBlank String clientId,
|
||||
@NotNull List<String> roleNames, @NotBlank String realmName) {
|
||||
private void revokeClientRolesFromUser(String userId, String clientId,
|
||||
List<String> roleNames, String realmName) {
|
||||
log.info("Révocation de {} rôles du client {} pour l'utilisateur {} dans le realm {}",
|
||||
roleNames.size(), clientId, userId, realmName);
|
||||
|
||||
@@ -431,17 +591,18 @@ public class RoleServiceImpl implements RoleService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RoleDTO> getUserClientRoles(@NotBlank String userId, @NotBlank String clientId,
|
||||
@NotBlank String realmName) {
|
||||
public List<RoleDTO> getUserClientRoles(@NotBlank String userId,
|
||||
@NotBlank String realmName,
|
||||
@NotBlank String clientName) {
|
||||
log.debug("Récupération des rôles du client {} pour l'utilisateur {} dans le realm {}",
|
||||
clientId, userId, realmName);
|
||||
clientName, userId, realmName);
|
||||
|
||||
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.clients();
|
||||
|
||||
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
||||
clientsResource.findByClientId(clientId);
|
||||
clientsResource.findByClientId(clientName);
|
||||
|
||||
if (clients.isEmpty()) {
|
||||
return List.of();
|
||||
@@ -457,86 +618,246 @@ public class RoleServiceImpl implements RoleService {
|
||||
.listAll();
|
||||
|
||||
List<RoleDTO> roles = RoleMapper.toDTOList(roleReps, realmName, TypeRole.CLIENT_ROLE);
|
||||
roles.forEach(role -> role.setClientId(clientId));
|
||||
roles.forEach(role -> role.setClientId(clientName));
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RoleDTO> getAllUserRoles(@NotBlank String userId, @NotBlank String realmName) {
|
||||
log.debug("Récupération de tous les rôles de l'utilisateur {} dans le realm {}", userId, realmName);
|
||||
|
||||
List<RoleDTO> allRoles = new ArrayList<>();
|
||||
|
||||
// Ajouter les rôles realm
|
||||
allRoles.addAll(getUserRealmRoles(userId, realmName));
|
||||
|
||||
// Ajouter les rôles client pour tous les clients
|
||||
try {
|
||||
var clientsResource = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.clients();
|
||||
|
||||
List<org.keycloak.representations.idm.ClientRepresentation> clients = clientsResource.findAll();
|
||||
|
||||
for (org.keycloak.representations.idm.ClientRepresentation client : clients) {
|
||||
String clientId = client.getClientId();
|
||||
allRoles.addAll(getUserClientRoles(userId, realmName, clientId));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Erreur lors de la récupération des rôles client pour l'utilisateur {}", userId, e);
|
||||
}
|
||||
|
||||
return allRoles;
|
||||
}
|
||||
|
||||
// ==================== Rôles composites ====================
|
||||
|
||||
@Override
|
||||
public void addCompositesToRealmRole(@NotBlank String roleName, @NotNull List<String> compositeRoleNames,
|
||||
@NotBlank String realmName) {
|
||||
log.info("Ajout de {} rôles composites au rôle realm {} dans le realm {}",
|
||||
compositeRoleNames.size(), roleName, realmName);
|
||||
public void addCompositeRoles(@NotBlank String parentRoleId,
|
||||
@NotNull List<String> childRoleIds,
|
||||
@NotBlank String realmName,
|
||||
@NotNull TypeRole typeRole,
|
||||
String clientName) {
|
||||
log.info("Ajout de {} rôles composites au rôle {} (type: {}) dans le realm {}",
|
||||
childRoleIds.size(), parentRoleId, typeRole, realmName);
|
||||
|
||||
RoleResource roleResource = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.roles()
|
||||
.get(roleName);
|
||||
// Trouver le nom du rôle parent par son ID
|
||||
Optional<RoleDTO> parentRole = getRoleById(parentRoleId, realmName, typeRole, clientName);
|
||||
if (parentRole.isEmpty()) {
|
||||
throw new jakarta.ws.rs.NotFoundException("Rôle parent non trouvé: " + parentRoleId);
|
||||
}
|
||||
String parentRoleName = parentRole.get().getName();
|
||||
|
||||
RolesResource rolesResource = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.roles();
|
||||
|
||||
List<RoleRepresentation> compositesToAdd = compositeRoleNames.stream()
|
||||
.map(compositeName -> {
|
||||
try {
|
||||
return rolesResource.get(compositeName).toRepresentation();
|
||||
} catch (NotFoundException e) {
|
||||
log.warn("Rôle composite {} non trouvé, ignoré", compositeName);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter(role -> role != null)
|
||||
.collect(Collectors.toList());
|
||||
if (typeRole == TypeRole.REALM_ROLE) {
|
||||
RoleResource roleResource = rolesResource.get(parentRoleName);
|
||||
|
||||
if (!compositesToAdd.isEmpty()) {
|
||||
roleResource.addComposites(compositesToAdd);
|
||||
// Convertir les IDs en noms de rôles
|
||||
List<String> childRoleNames = childRoleIds.stream()
|
||||
.map(childRoleId -> {
|
||||
Optional<RoleDTO> childRole = getRealmRoleById(childRoleId, realmName);
|
||||
return childRole.map(RoleDTO::getName).orElse(null);
|
||||
})
|
||||
.filter(name -> name != null)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<RoleRepresentation> compositesToAdd = childRoleNames.stream()
|
||||
.map(compositeName -> {
|
||||
try {
|
||||
return rolesResource.get(compositeName).toRepresentation();
|
||||
} catch (NotFoundException e) {
|
||||
log.warn("Rôle composite {} non trouvé, ignoré", compositeName);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter(role -> role != null)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!compositesToAdd.isEmpty()) {
|
||||
roleResource.addComposites(compositesToAdd);
|
||||
}
|
||||
} else if (typeRole == TypeRole.CLIENT_ROLE && clientName != null) {
|
||||
// Pour les rôles client, utiliser le client
|
||||
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.clients();
|
||||
|
||||
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
||||
clientsResource.findByClientId(clientName);
|
||||
|
||||
if (clients.isEmpty()) {
|
||||
throw new IllegalArgumentException("Client " + clientName + " non trouvé");
|
||||
}
|
||||
|
||||
String internalClientId = clients.get(0).getId();
|
||||
RolesResource clientRolesResource = clientsResource.get(internalClientId).roles();
|
||||
RoleResource roleResource = clientRolesResource.get(parentRoleName);
|
||||
|
||||
// Convertir les IDs en noms de rôles
|
||||
List<String> childRoleNames = childRoleIds.stream()
|
||||
.map(childRoleId -> {
|
||||
Optional<RoleDTO> childRole = getRoleById(childRoleId, realmName, typeRole, clientName);
|
||||
return childRole.map(RoleDTO::getName).orElse(null);
|
||||
})
|
||||
.filter(name -> name != null)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<RoleRepresentation> compositesToAdd = childRoleNames.stream()
|
||||
.map(compositeName -> {
|
||||
try {
|
||||
return clientRolesResource.get(compositeName).toRepresentation();
|
||||
} catch (NotFoundException e) {
|
||||
log.warn("Rôle composite {} non trouvé, ignoré", compositeName);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter(role -> role != null)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!compositesToAdd.isEmpty()) {
|
||||
roleResource.addComposites(compositesToAdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCompositesFromRealmRole(@NotBlank String roleName, @NotNull List<String> compositeRoleNames,
|
||||
@NotBlank String realmName) {
|
||||
log.info("Suppression de {} rôles composites du rôle realm {} dans le realm {}",
|
||||
compositeRoleNames.size(), roleName, realmName);
|
||||
public void removeCompositeRoles(@NotBlank String parentRoleId,
|
||||
@NotNull List<String> childRoleIds,
|
||||
@NotBlank String realmName,
|
||||
@NotNull TypeRole typeRole,
|
||||
String clientName) {
|
||||
log.info("Suppression de {} rôles composites du rôle {} (type: {}) dans le realm {}",
|
||||
childRoleIds.size(), parentRoleId, typeRole, realmName);
|
||||
|
||||
RoleResource roleResource = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.roles()
|
||||
.get(roleName);
|
||||
// Trouver le nom du rôle parent par son ID
|
||||
Optional<RoleDTO> parentRole = getRoleById(parentRoleId, realmName, typeRole, clientName);
|
||||
if (parentRole.isEmpty()) {
|
||||
throw new jakarta.ws.rs.NotFoundException("Rôle parent non trouvé: " + parentRoleId);
|
||||
}
|
||||
String parentRoleName = parentRole.get().getName();
|
||||
|
||||
RolesResource rolesResource = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.roles();
|
||||
|
||||
List<RoleRepresentation> compositesToRemove = compositeRoleNames.stream()
|
||||
.map(compositeName -> {
|
||||
try {
|
||||
return rolesResource.get(compositeName).toRepresentation();
|
||||
} catch (NotFoundException e) {
|
||||
log.warn("Rôle composite {} non trouvé, ignoré", compositeName);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter(role -> role != null)
|
||||
.collect(Collectors.toList());
|
||||
if (typeRole == TypeRole.REALM_ROLE) {
|
||||
RoleResource roleResource = rolesResource.get(parentRoleName);
|
||||
|
||||
if (!compositesToRemove.isEmpty()) {
|
||||
roleResource.deleteComposites(compositesToRemove);
|
||||
// Convertir les IDs en noms de rôles
|
||||
List<String> childRoleNames = childRoleIds.stream()
|
||||
.map(childRoleId -> {
|
||||
Optional<RoleDTO> childRole = getRealmRoleById(childRoleId, realmName);
|
||||
return childRole.map(RoleDTO::getName).orElse(null);
|
||||
})
|
||||
.filter(name -> name != null)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<RoleRepresentation> compositesToRemove = childRoleNames.stream()
|
||||
.map(compositeName -> {
|
||||
try {
|
||||
return rolesResource.get(compositeName).toRepresentation();
|
||||
} catch (NotFoundException e) {
|
||||
log.warn("Rôle composite {} non trouvé, ignoré", compositeName);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter(role -> role != null)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!compositesToRemove.isEmpty()) {
|
||||
roleResource.deleteComposites(compositesToRemove);
|
||||
}
|
||||
} else if (typeRole == TypeRole.CLIENT_ROLE && clientName != null) {
|
||||
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.clients();
|
||||
|
||||
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
||||
clientsResource.findByClientId(clientName);
|
||||
|
||||
if (clients.isEmpty()) {
|
||||
throw new IllegalArgumentException("Client " + clientName + " non trouvé");
|
||||
}
|
||||
|
||||
String internalClientId = clients.get(0).getId();
|
||||
RolesResource clientRolesResource = clientsResource.get(internalClientId).roles();
|
||||
RoleResource roleResource = clientRolesResource.get(parentRoleName);
|
||||
|
||||
// Convertir les IDs en noms de rôles
|
||||
List<String> childRoleNames = childRoleIds.stream()
|
||||
.map(childRoleId -> {
|
||||
Optional<RoleDTO> childRole = getRoleById(childRoleId, realmName, typeRole, clientName);
|
||||
return childRole.map(RoleDTO::getName).orElse(null);
|
||||
})
|
||||
.filter(name -> name != null)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<RoleRepresentation> compositesToRemove = childRoleNames.stream()
|
||||
.map(compositeName -> {
|
||||
try {
|
||||
return clientRolesResource.get(compositeName).toRepresentation();
|
||||
} catch (NotFoundException e) {
|
||||
log.warn("Rôle composite {} non trouvé, ignoré", compositeName);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter(role -> role != null)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (!compositesToRemove.isEmpty()) {
|
||||
roleResource.deleteComposites(compositesToRemove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RoleDTO> getCompositeRoles(@NotBlank String roleName, @NotBlank String realmName) {
|
||||
log.debug("Récupération des rôles composites du rôle {} dans le realm {}", roleName, realmName);
|
||||
|
||||
List<RoleRepresentation> composites = keycloakAdminClient.getInstance()
|
||||
@Override
|
||||
public List<RoleDTO> getCompositeRoles(@NotBlank String roleId,
|
||||
@NotBlank String realmName,
|
||||
@NotNull TypeRole typeRole,
|
||||
String clientName) {
|
||||
log.debug("Récupération des rôles composites du rôle {} dans le realm {}", roleId, realmName);
|
||||
|
||||
// Pour récupérer par ID, on doit d'abord trouver le nom du rôle
|
||||
// Comme Keycloak ne permet pas de récupérer directement par ID, on doit lister et trouver
|
||||
RolesResource rolesResource = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.roles()
|
||||
.get(roleName)
|
||||
.roles();
|
||||
|
||||
RoleRepresentation roleRep = rolesResource.list().stream()
|
||||
.filter(r -> r.getId().equals(roleId))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new jakarta.ws.rs.NotFoundException("Rôle non trouvé: " + roleId));
|
||||
|
||||
java.util.Set<RoleRepresentation> compositesSet = rolesResource
|
||||
.get(roleRep.getName())
|
||||
.getRoleComposites();
|
||||
|
||||
List<RoleRepresentation> composites = new ArrayList<>(compositesSet);
|
||||
|
||||
return RoleMapper.toDTOList(composites, realmName, TypeRole.COMPOSITE_ROLE);
|
||||
}
|
||||
@@ -544,66 +865,111 @@ public class RoleServiceImpl implements RoleService {
|
||||
// ==================== Vérification de permissions ====================
|
||||
|
||||
@Override
|
||||
public boolean userHasRealmRole(@NotBlank String userId, @NotBlank String roleName,
|
||||
@NotBlank String realmName) {
|
||||
log.debug("Vérification si l'utilisateur {} a le rôle realm {} dans le realm {}",
|
||||
userId, roleName, realmName);
|
||||
public boolean userHasRole(@NotBlank String userId,
|
||||
@NotBlank String roleName,
|
||||
@NotBlank String realmName,
|
||||
@NotNull TypeRole typeRole,
|
||||
String clientName) {
|
||||
log.debug("Vérification si l'utilisateur {} a le rôle {} (type: {}) dans le realm {}",
|
||||
userId, roleName, typeRole, realmName);
|
||||
|
||||
List<RoleRepresentation> userRoles = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.users()
|
||||
.get(userId)
|
||||
.roles()
|
||||
.realmLevel()
|
||||
.listEffective(); // Incluant les rôles hérités via composites
|
||||
if (typeRole == TypeRole.REALM_ROLE) {
|
||||
List<RoleRepresentation> userRoles = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.users()
|
||||
.get(userId)
|
||||
.roles()
|
||||
.realmLevel()
|
||||
.listEffective(); // Incluant les rôles hérités via composites
|
||||
|
||||
return userRoles.stream()
|
||||
.anyMatch(role -> role.getName().equals(roleName));
|
||||
}
|
||||
return userRoles.stream()
|
||||
.anyMatch(role -> role.getName().equals(roleName));
|
||||
} else if (typeRole == TypeRole.CLIENT_ROLE && clientName != null) {
|
||||
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.clients();
|
||||
|
||||
@Override
|
||||
public boolean userHasClientRole(@NotBlank String userId, @NotBlank String clientId,
|
||||
@NotBlank String roleName, @NotBlank String realmName) {
|
||||
log.debug("Vérification si l'utilisateur {} a le rôle client {} du client {} dans le realm {}",
|
||||
userId, roleName, clientId, realmName);
|
||||
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
||||
clientsResource.findByClientId(clientName);
|
||||
|
||||
ClientsResource clientsResource = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.clients();
|
||||
if (clients.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<org.keycloak.representations.idm.ClientRepresentation> clients =
|
||||
clientsResource.findByClientId(clientId);
|
||||
String internalClientId = clients.get(0).getId();
|
||||
List<RoleRepresentation> userClientRoles = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.users()
|
||||
.get(userId)
|
||||
.roles()
|
||||
.clientLevel(internalClientId)
|
||||
.listEffective();
|
||||
|
||||
if (clients.isEmpty()) {
|
||||
return false;
|
||||
return userClientRoles.stream()
|
||||
.anyMatch(role -> role.getName().equals(roleName));
|
||||
}
|
||||
|
||||
String internalClientId = clients.get(0).getId();
|
||||
List<RoleRepresentation> userClientRoles = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.users()
|
||||
.get(userId)
|
||||
.roles()
|
||||
.clientLevel(internalClientId)
|
||||
.listEffective();
|
||||
|
||||
return userClientRoles.stream()
|
||||
.anyMatch(role -> role.getName().equals(roleName));
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RoleDTO> getUserEffectiveRealmRoles(@NotBlank String userId, @NotBlank String realmName) {
|
||||
log.debug("Récupération des rôles realm effectifs de l'utilisateur {} dans le realm {}",
|
||||
userId, realmName);
|
||||
public boolean roleExists(@NotBlank String roleName,
|
||||
@NotBlank String realmName,
|
||||
@NotNull TypeRole typeRole,
|
||||
String clientName) {
|
||||
log.debug("Vérification de l'existence du rôle {} (type: {}) dans le realm {}",
|
||||
roleName, typeRole, realmName);
|
||||
|
||||
List<RoleRepresentation> effectiveRoles = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.users()
|
||||
.get(userId)
|
||||
.roles()
|
||||
.realmLevel()
|
||||
.listEffective();
|
||||
return getRoleByName(roleName, realmName, typeRole, clientName).isPresent();
|
||||
}
|
||||
|
||||
return RoleMapper.toDTOList(effectiveRoles, realmName, TypeRole.REALM_ROLE);
|
||||
@Override
|
||||
public long countUsersWithRole(@NotBlank String roleId,
|
||||
@NotBlank String realmName,
|
||||
@NotNull TypeRole typeRole,
|
||||
String clientName) {
|
||||
log.debug("Comptage des utilisateurs ayant le rôle {} (type: {}) dans le realm {}",
|
||||
roleId, typeRole, realmName);
|
||||
|
||||
// Trouver le nom du rôle par son ID
|
||||
Optional<RoleDTO> role = getRoleById(roleId, realmName, typeRole, clientName);
|
||||
if (role.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
String roleName = role.get().getName();
|
||||
|
||||
try {
|
||||
// Keycloak ne fournit pas directement cette fonctionnalité via l'API Admin
|
||||
// On doit lister tous les utilisateurs et vérifier leurs rôles
|
||||
// C'est coûteux mais nécessaire
|
||||
List<UserRepresentation> users = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.users()
|
||||
.list();
|
||||
|
||||
long count = 0;
|
||||
for (UserRepresentation user : users) {
|
||||
if (userHasRole(user.getId(), roleName, realmName, typeRole, clientName)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors du comptage des utilisateurs avec le rôle {}", roleId, e);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Méthodes privées pour compatibilité interne (utilisées par les nouvelles méthodes publiques)
|
||||
private boolean userHasRealmRole(String userId, String roleName,
|
||||
String realmName) {
|
||||
return userHasRole(userId, roleName, realmName, TypeRole.REALM_ROLE, null);
|
||||
}
|
||||
|
||||
private boolean userHasClientRole(String userId, String clientId,
|
||||
String roleName, String realmName) {
|
||||
return userHasRole(userId, roleName, realmName, TypeRole.CLIENT_ROLE, clientId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
package dev.lions.user.manager.service.impl;
|
||||
|
||||
import dev.lions.user.manager.client.KeycloakAdminClient;
|
||||
import dev.lions.user.manager.dto.role.RoleDTO;
|
||||
import dev.lions.user.manager.dto.sync.HealthStatusDTO;
|
||||
import dev.lions.user.manager.dto.sync.SyncResultDTO;
|
||||
import dev.lions.user.manager.dto.user.UserDTO;
|
||||
import dev.lions.user.manager.enums.role.TypeRole;
|
||||
import dev.lions.user.manager.mapper.RoleMapper;
|
||||
import dev.lions.user.manager.mapper.UserMapper;
|
||||
import dev.lions.user.manager.service.SyncService;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Implémentation du service de synchronisation avec Keycloak
|
||||
*
|
||||
* Ce service permet de:
|
||||
* - Synchroniser les utilisateurs depuis Keycloak
|
||||
* - Synchroniser les rôles depuis Keycloak
|
||||
* - Vérifier la cohérence des données
|
||||
* - Effectuer des health checks sur Keycloak
|
||||
*/
|
||||
@ApplicationScoped
|
||||
@Slf4j
|
||||
public class SyncServiceImpl implements SyncService {
|
||||
|
||||
@Inject
|
||||
KeycloakAdminClient keycloakAdminClient;
|
||||
|
||||
@Override
|
||||
public int syncUsersFromRealm(@NotBlank String realmName) {
|
||||
log.info("Synchronisation des utilisateurs depuis le realm: {}", realmName);
|
||||
|
||||
try {
|
||||
List<UserRepresentation> userReps = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.users()
|
||||
.list();
|
||||
|
||||
int count = userReps.size();
|
||||
log.info("✅ {} utilisateurs synchronisés depuis le realm {}", count, realmName);
|
||||
return count;
|
||||
} catch (Exception e) {
|
||||
log.error("❌ Erreur lors de la synchronisation des utilisateurs depuis le realm {}", realmName, e);
|
||||
throw new RuntimeException("Erreur lors de la synchronisation des utilisateurs", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int syncRolesFromRealm(@NotBlank String realmName) {
|
||||
log.info("Synchronisation des rôles depuis le realm: {}", realmName);
|
||||
|
||||
try {
|
||||
List<RoleRepresentation> roleReps = keycloakAdminClient.getInstance()
|
||||
.realm(realmName)
|
||||
.roles()
|
||||
.list();
|
||||
|
||||
int count = roleReps.size();
|
||||
log.info("✅ {} rôles synchronisés depuis le realm {}", count, realmName);
|
||||
return count;
|
||||
} catch (Exception e) {
|
||||
log.error("❌ Erreur lors de la synchronisation des rôles depuis le realm {}", realmName, e);
|
||||
throw new RuntimeException("Erreur lors de la synchronisation des rôles", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Integer> syncAllRealms() {
|
||||
log.info("Synchronisation de tous les realms");
|
||||
|
||||
Map<String, Integer> results = new java.util.HashMap<>();
|
||||
|
||||
try {
|
||||
// Lister tous les realms
|
||||
List<org.keycloak.representations.idm.RealmRepresentation> realms =
|
||||
keycloakAdminClient.getInstance().realms().findAll();
|
||||
|
||||
for (org.keycloak.representations.idm.RealmRepresentation realm : realms) {
|
||||
String realmName = realm.getRealm();
|
||||
try {
|
||||
int usersCount = syncUsersFromRealm(realmName);
|
||||
int rolesCount = syncRolesFromRealm(realmName);
|
||||
results.put(realmName, usersCount + rolesCount);
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la synchronisation du realm {}", realmName, e);
|
||||
results.put(realmName, 0);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la synchronisation de tous les realms", e);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> checkDataConsistency(@NotBlank String realmName) {
|
||||
log.info("Vérification de la cohérence des données pour le realm: {}", realmName);
|
||||
|
||||
Map<String, Object> report = new java.util.HashMap<>();
|
||||
|
||||
try {
|
||||
// Pour l'instant, on retourne juste un rapport basique
|
||||
// En production, on comparerait avec un cache local
|
||||
report.put("realmName", realmName);
|
||||
report.put("status", "ok");
|
||||
report.put("message", "Cohérence vérifiée");
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la vérification de cohérence pour le realm {}", realmName, e);
|
||||
report.put("status", "error");
|
||||
report.put("message", e.getMessage());
|
||||
}
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> forceSyncRealm(@NotBlank String realmName) {
|
||||
log.info("Synchronisation forcée du realm: {}", realmName);
|
||||
|
||||
Map<String, Object> stats = new java.util.HashMap<>();
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
int usersCount = syncUsersFromRealm(realmName);
|
||||
int rolesCount = syncRolesFromRealm(realmName);
|
||||
|
||||
stats.put("realmName", realmName);
|
||||
stats.put("usersCount", usersCount);
|
||||
stats.put("rolesCount", rolesCount);
|
||||
stats.put("success", true);
|
||||
stats.put("durationMs", System.currentTimeMillis() - startTime);
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la synchronisation forcée du realm {}", realmName, e);
|
||||
stats.put("success", false);
|
||||
stats.put("error", e.getMessage());
|
||||
stats.put("durationMs", System.currentTimeMillis() - startTime);
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getLastSyncStatus(@NotBlank String realmName) {
|
||||
log.debug("Récupération du statut de la dernière synchronisation pour le realm: {}", realmName);
|
||||
|
||||
Map<String, Object> status = new java.util.HashMap<>();
|
||||
status.put("realmName", realmName);
|
||||
status.put("lastSyncTime", System.currentTimeMillis()); // En production, récupérer depuis un cache
|
||||
status.put("status", "completed");
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isKeycloakAvailable() {
|
||||
log.debug("Vérification de la disponibilité de Keycloak");
|
||||
|
||||
try {
|
||||
// Test de connexion en récupérant les informations du serveur
|
||||
keycloakAdminClient.getInstance().serverInfo().getInfo();
|
||||
log.debug("✅ Keycloak est accessible et fonctionne");
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("❌ Keycloak n'est pas accessible: {}", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getKeycloakHealthInfo() {
|
||||
log.info("Récupération du statut de santé complet de Keycloak");
|
||||
|
||||
Map<String, Object> healthInfo = new java.util.HashMap<>();
|
||||
healthInfo.put("timestamp", System.currentTimeMillis());
|
||||
|
||||
try {
|
||||
// Test connexion principale
|
||||
var serverInfo = keycloakAdminClient.getInstance().serverInfo().getInfo();
|
||||
healthInfo.put("keycloakAccessible", true);
|
||||
healthInfo.put("keycloakVersion", serverInfo.getSystemInfo().getVersion());
|
||||
|
||||
// Test des realms (on essaie juste de lister)
|
||||
try {
|
||||
int realmsCount = keycloakAdminClient.getInstance().realms().findAll().size();
|
||||
healthInfo.put("realmsAccessible", true);
|
||||
healthInfo.put("realmsCount", realmsCount);
|
||||
} catch (Exception e) {
|
||||
healthInfo.put("realmsAccessible", false);
|
||||
log.warn("Impossible d'accéder aux realms: {}", e.getMessage());
|
||||
}
|
||||
|
||||
healthInfo.put("overallHealthy", true);
|
||||
log.info("✅ Keycloak est en bonne santé - Version: {}, Realms: {}",
|
||||
healthInfo.get("keycloakVersion"), healthInfo.get("realmsCount"));
|
||||
|
||||
} catch (Exception e) {
|
||||
healthInfo.put("keycloakAccessible", false);
|
||||
healthInfo.put("overallHealthy", false);
|
||||
healthInfo.put("errorMessage", e.getMessage());
|
||||
log.error("❌ Keycloak n'est pas accessible: {}", e.getMessage());
|
||||
}
|
||||
|
||||
return healthInfo;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.keycloak.admin.client.resource.UserResource;
|
||||
import org.keycloak.admin.client.resource.UsersResource;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -99,11 +100,35 @@ public class UserServiceImpl implements UserService {
|
||||
try {
|
||||
UserResource userResource = keycloakAdminClient.getUsers(realmName).get(userId);
|
||||
UserRepresentation userRep = userResource.toRepresentation();
|
||||
return Optional.of(UserMapper.toDTO(userRep, realmName));
|
||||
UserDTO userDTO = UserMapper.toDTO(userRep, realmName);
|
||||
|
||||
// Récupérer les rôles realm de l'utilisateur
|
||||
try {
|
||||
List<RoleRepresentation> realmRoles = userResource.roles().realmLevel().listAll();
|
||||
if (realmRoles != null && !realmRoles.isEmpty()) {
|
||||
List<String> roleNames = realmRoles.stream()
|
||||
.map(RoleRepresentation::getName)
|
||||
.collect(Collectors.toList());
|
||||
userDTO.setRealmRoles(roleNames);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("Erreur lors de la récupération des rôles realm pour l'utilisateur {}: {}", userId, e.getMessage());
|
||||
// Ne pas échouer si les rôles ne peuvent pas être récupérés
|
||||
}
|
||||
|
||||
return Optional.of(userDTO);
|
||||
} catch (NotFoundException e) {
|
||||
log.warn("Utilisateur {} non trouvé dans le realm {}", userId, realmName);
|
||||
return Optional.empty();
|
||||
} catch (Exception e) {
|
||||
// Vérifier si l'exception contient un message indiquant un 404
|
||||
String errorMessage = e.getMessage();
|
||||
if (errorMessage != null && (errorMessage.contains("404") ||
|
||||
errorMessage.contains("Server response is: 404") ||
|
||||
errorMessage.contains("Received: 'Server response is: 404'"))) {
|
||||
log.warn("Utilisateur {} non trouvé dans le realm {} (404 détecté dans l'exception)", userId, realmName);
|
||||
return Optional.empty();
|
||||
}
|
||||
log.error("Erreur lors de la récupération de l'utilisateur {}", userId, e);
|
||||
throw new RuntimeException("Impossible de récupérer l'utilisateur", e);
|
||||
}
|
||||
@@ -425,7 +450,7 @@ public class UserServiceImpl implements UserService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int importUsersFromCSV(@NotBlank String csvContent, @NotBlank String realmName) {
|
||||
public dev.lions.user.manager.dto.importexport.ImportResultDTO importUsersFromCSV(@NotBlank String csvContent, @NotBlank String realmName) {
|
||||
// TODO: Implémenter l'import CSV
|
||||
throw new UnsupportedOperationException("Import CSV non implémenté");
|
||||
}
|
||||
|
||||
@@ -11,11 +11,20 @@ quarkus.http.cors.methods=GET,POST,PUT,DELETE,PATCH,OPTIONS
|
||||
quarkus.http.cors.headers=*
|
||||
|
||||
# Keycloak OIDC Configuration (DEV)
|
||||
quarkus.oidc.auth-server-url=http://localhost:8180/realms/master
|
||||
quarkus.oidc.client-id=lions-user-manager
|
||||
quarkus.oidc.credentials.secret=dev-secret-change-me
|
||||
quarkus.oidc.tls.verification=none
|
||||
# Le backend vérifie les tokens JWT envoyés par le client
|
||||
# IMPORTANT: Pour un service, Quarkus valide les tokens JWT sans avoir besoin d'un client-id/secret
|
||||
# Le backend accepte les tokens émis pour n'importe quel client du realm
|
||||
quarkus.oidc.enabled=true
|
||||
quarkus.oidc.auth-server-url=http://localhost:8180/realms/lions-user-manager
|
||||
quarkus.oidc.application-type=service
|
||||
quarkus.oidc.tls.verification=none
|
||||
quarkus.oidc.token.issuer=http://localhost:8180/realms/lions-user-manager
|
||||
quarkus.oidc.discovery-enabled=true
|
||||
# Accepter les tokens avec audience "account" (audience par défaut de Keycloak)
|
||||
# Cela permet d'accepter les tokens émis pour le frontend sans configuration Keycloak supplémentaire
|
||||
quarkus.oidc.token.audience=account
|
||||
# Vérifier le token (obligatoire pour un service)
|
||||
quarkus.oidc.verify-access-token=true
|
||||
|
||||
# Keycloak Admin Client Configuration (DEV)
|
||||
lions.keycloak.server-url=http://localhost:8180
|
||||
@@ -27,7 +36,7 @@ lions.keycloak.connection-pool-size=5
|
||||
lions.keycloak.timeout-seconds=30
|
||||
|
||||
# Realms autorisés (DEV)
|
||||
lions.keycloak.authorized-realms=btpxpress,master,lions-realm,test-realm
|
||||
lions.keycloak.authorized-realms=lions-user-manager,master,btpxpress,test-realm
|
||||
|
||||
# Circuit Breaker Configuration (DEV - plus permissif)
|
||||
quarkus.smallrye-fault-tolerance.enabled=true
|
||||
@@ -52,14 +61,17 @@ lions.audit.retention-days=30
|
||||
#quarkus.flyway.migrate-at-start=false
|
||||
|
||||
# Logging Configuration (DEV)
|
||||
quarkus.log.level=DEBUG
|
||||
quarkus.log.level=INFO
|
||||
quarkus.log.category."dev.lions.user.manager".level=DEBUG
|
||||
quarkus.log.category."org.keycloak".level=INFO
|
||||
quarkus.log.category."io.quarkus".level=INFO
|
||||
# Logging OIDC pour debug
|
||||
quarkus.log.category."io.quarkus.oidc".level=DEBUG
|
||||
quarkus.log.category."io.quarkus.security".level=DEBUG
|
||||
|
||||
quarkus.log.console.enable=true
|
||||
quarkus.log.console.format=%d{HH:mm:ss} %-5p [%c{2.}] (%t) %s%e%n
|
||||
quarkus.log.console.color=true
|
||||
# quarkus.log.console.color est déprécié dans Quarkus 3.x
|
||||
|
||||
# File Logging pour Audit (DEV)
|
||||
quarkus.log.file.enable=true
|
||||
@@ -69,14 +81,48 @@ quarkus.log.file.rotation.max-backup-index=3
|
||||
|
||||
# OpenAPI/Swagger Configuration (DEV - toujours activé)
|
||||
quarkus.swagger-ui.always-include=true
|
||||
quarkus.swagger-ui.path=/swagger-ui
|
||||
quarkus.swagger-ui.enable=true
|
||||
# Le chemin par défaut est /q/swagger-ui (pas besoin de le spécifier)
|
||||
|
||||
# Dev Services (activé en DEV)
|
||||
quarkus.devservices.enabled=false
|
||||
|
||||
# Security Configuration (DEV - plus permissif)
|
||||
# Security Configuration (DEV)
|
||||
# La sécurité est activée - les rôles sont vérifiés via OIDC/Keycloak
|
||||
# Note: KeycloakTestUserConfig configure automatiquement l'utilisateur de test au démarrage
|
||||
quarkus.security.auth.enabled=true
|
||||
quarkus.security.jaxrs.deny-unannotated-endpoints=false
|
||||
quarkus.security.auth.proactive=false
|
||||
|
||||
# Configuration OIDC - Extraction des rôles
|
||||
# Le backend extrait les rôles depuis realm_access/roles (standard Keycloak)
|
||||
# Le scope "roles" de Keycloak crée automatiquement realm_access.roles
|
||||
# Syntaxe Quarkus: utiliser un slash pour les chemins imbriqués
|
||||
quarkus.oidc.roles.role-claim-path=realm_access/roles
|
||||
|
||||
# Définir explicitement le profil pour que DevSecurityContextProducer le détecte
|
||||
quarkus.profile=dev
|
||||
|
||||
# Logging pour debug du filtre de sécurité
|
||||
quarkus.log.category."dev.lions.user.manager.security".level=DEBUG
|
||||
|
||||
# Logging OIDC et Security pour debug
|
||||
quarkus.log.category."io.quarkus.oidc".level=DEBUG
|
||||
quarkus.log.category."io.quarkus.oidc.runtime".level=DEBUG
|
||||
quarkus.log.category."io.quarkus.security".level=DEBUG
|
||||
quarkus.log.category."io.quarkus.security.runtime".level=DEBUG
|
||||
|
||||
# Hot Reload
|
||||
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
|
||||
|
||||
@@ -1,113 +1,113 @@
|
||||
# ============================================================================
|
||||
# Lions User Manager - Server Implementation Configuration - PRODUCTION
|
||||
# Lions User Manager Server - Configuration Production
|
||||
# ============================================================================
|
||||
# Ce fichier contient TOUTES les propriétés spécifiques à la production
|
||||
# Il surcharge et complète application.properties
|
||||
# ============================================================================
|
||||
|
||||
# HTTP Configuration
|
||||
quarkus.http.port=8081
|
||||
quarkus.http.host=0.0.0.0
|
||||
quarkus.http.cors=true
|
||||
quarkus.http.cors.origins=https://btpxpress.lions.dev,https://admin.lions.dev
|
||||
quarkus.http.cors.methods=GET,POST,PUT,DELETE,PATCH,OPTIONS
|
||||
quarkus.http.cors.headers=*
|
||||
# ============================================
|
||||
# HTTP Configuration PROD
|
||||
# ============================================
|
||||
quarkus.http.port=8080
|
||||
|
||||
# Keycloak OIDC Configuration (PROD)
|
||||
quarkus.oidc.auth-server-url=https://security.lions.dev/realms/master
|
||||
quarkus.oidc.client-id=lions-user-manager
|
||||
quarkus.oidc.credentials.secret=${KEYCLOAK_CLIENT_SECRET}
|
||||
quarkus.oidc.tls.verification=required
|
||||
quarkus.oidc.application-type=service
|
||||
# CORS restrictif en production (via variable d'environnement) - autoriser le frontend users.lions.dev
|
||||
quarkus.http.cors.origins=${CORS_ORIGINS:https://users.lions.dev,https://btpxpress.lions.dev,https://admin.lions.dev}
|
||||
|
||||
# Keycloak Admin Client Configuration (PROD)
|
||||
lions.keycloak.server-url=https://security.lions.dev
|
||||
lions.keycloak.admin-realm=master
|
||||
lions.keycloak.admin-client-id=admin-cli
|
||||
lions.keycloak.admin-username=${KEYCLOAK_ADMIN_USERNAME}
|
||||
lions.keycloak.admin-password=${KEYCLOAK_ADMIN_PASSWORD}
|
||||
lions.keycloak.connection-pool-size=20
|
||||
lions.keycloak.timeout-seconds=60
|
||||
|
||||
# Realms autorisés (PROD)
|
||||
lions.keycloak.authorized-realms=btpxpress,lions-realm
|
||||
|
||||
# Circuit Breaker Configuration (PROD - strict)
|
||||
quarkus.smallrye-fault-tolerance.enabled=true
|
||||
|
||||
# Retry Configuration (PROD)
|
||||
lions.keycloak.retry.max-attempts=5
|
||||
lions.keycloak.retry.delay-seconds=3
|
||||
|
||||
# Audit Configuration (PROD)
|
||||
lions.audit.enabled=true
|
||||
lions.audit.log-to-database=true
|
||||
lions.audit.log-to-file=true
|
||||
lions.audit.retention-days=365
|
||||
|
||||
# Database Configuration (PROD - obligatoire pour audit)
|
||||
quarkus.datasource.db-kind=postgresql
|
||||
quarkus.datasource.username=${DB_USERNAME:audit_user}
|
||||
quarkus.datasource.password=${DB_PASSWORD}
|
||||
quarkus.datasource.jdbc.url=jdbc:postgresql://${DB_HOST:lions-db.lions.svc.cluster.local}:${DB_PORT:5432}/${DB_NAME:lions_audit}
|
||||
quarkus.datasource.jdbc.max-size=20
|
||||
quarkus.datasource.jdbc.min-size=5
|
||||
quarkus.hibernate-orm.database.generation=none
|
||||
quarkus.flyway.migrate-at-start=true
|
||||
quarkus.flyway.baseline-on-migrate=true
|
||||
quarkus.flyway.baseline-version=1.0.0
|
||||
|
||||
# Logging Configuration (PROD)
|
||||
# ============================================
|
||||
# Logging PROD (moins verbeux)
|
||||
# ============================================
|
||||
quarkus.log.level=INFO
|
||||
quarkus.log.category."dev.lions.user.manager".level=INFO
|
||||
quarkus.log.category."org.keycloak".level=WARN
|
||||
quarkus.log.category."io.quarkus".level=WARN
|
||||
|
||||
quarkus.log.console.enable=true
|
||||
quarkus.log.console.format=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c{3.}] (%t) %s%e%n
|
||||
quarkus.log.console.json=true
|
||||
|
||||
# File Logging pour Audit (PROD)
|
||||
quarkus.log.file.enable=true
|
||||
quarkus.log.file.path=/var/log/lions/lions-user-manager.log
|
||||
quarkus.log.file.rotation.max-file-size=50M
|
||||
quarkus.log.file.rotation.max-backup-index=30
|
||||
quarkus.log.file.rotation.rotate-on-boot=false
|
||||
# File Logging désactivé en prod Kubernetes (utilise stdout pour logs centralisés)
|
||||
# quarkus.log.file.path=/var/log/lions/lions-user-manager.log
|
||||
# quarkus.log.file.rotation.max-file-size=50M
|
||||
# quarkus.log.file.rotation.max-backup-index=30
|
||||
# quarkus.log.file.rotation.rotate-on-boot=false
|
||||
|
||||
# OpenAPI/Swagger Configuration (PROD - désactivé par défaut)
|
||||
quarkus.swagger-ui.always-include=false
|
||||
quarkus.swagger-ui.path=/swagger-ui
|
||||
# ============================================
|
||||
# OIDC Configuration PROD - OBLIGATOIRE ET ACTIF
|
||||
# ============================================
|
||||
quarkus.oidc.enabled=true
|
||||
quarkus.oidc.auth-server-url=${KEYCLOAK_AUTH_SERVER_URL:https://security.lions.dev/realms/lions-user-manager}
|
||||
quarkus.oidc.client-id=${KEYCLOAK_CLIENT_ID:lions-user-manager-server}
|
||||
# Client bearer-only - pas de secret nécessaire
|
||||
# quarkus.oidc.credentials.secret=${KEYCLOAK_CLIENT_SECRET}
|
||||
quarkus.oidc.token.issuer=${KEYCLOAK_AUTH_SERVER_URL:https://security.lions.dev/realms/lions-user-manager}
|
||||
|
||||
# Vérification TLS requise en production
|
||||
quarkus.oidc.tls.verification=required
|
||||
|
||||
# Vérification stricte des tokens
|
||||
quarkus.oidc.discovery-enabled=true
|
||||
# quarkus.oidc.verify-access-token=true # Propriété non reconnue
|
||||
|
||||
# Extraction des rôles
|
||||
quarkus.oidc.roles.role-claim-path=realm_access/roles
|
||||
|
||||
# ============================================
|
||||
# Keycloak Admin Client Configuration PROD
|
||||
# ============================================
|
||||
lions.keycloak.server-url=${KEYCLOAK_SERVER_URL:https://security.lions.dev}
|
||||
lions.keycloak.admin-realm=${KEYCLOAK_ADMIN_REALM:master}
|
||||
lions.keycloak.admin-client-id=admin-cli
|
||||
lions.keycloak.admin-username=${KEYCLOAK_ADMIN_USERNAME:admin}
|
||||
lions.keycloak.admin-password=${KEYCLOAK_ADMIN_PASSWORD:KeycloakAdmin2025!}
|
||||
|
||||
# Pool de connexions augmenté en production
|
||||
lions.keycloak.connection-pool-size=20
|
||||
lions.keycloak.timeout-seconds=60
|
||||
|
||||
# Realms autorisés en production (via variable d'environnement)
|
||||
lions.keycloak.authorized-realms=${KEYCLOAK_AUTHORIZED_REALMS:lions-user-manager,btpxpress,master,unionflow}
|
||||
|
||||
# ============================================
|
||||
# Retry Configuration PROD
|
||||
# ============================================
|
||||
lions.keycloak.retry.max-attempts=5
|
||||
lions.keycloak.retry.delay-seconds=3
|
||||
|
||||
# ============================================
|
||||
# Audit Configuration PROD
|
||||
# ============================================
|
||||
lions.audit.retention-days=365
|
||||
lions.audit.log-to-database=true
|
||||
|
||||
# ============================================
|
||||
# Database Configuration PROD - Désactivé complètement
|
||||
# ============================================
|
||||
quarkus.datasource.devservices.enabled=false
|
||||
quarkus.datasource.health.enabled=false
|
||||
|
||||
# ============================================
|
||||
# OpenAPI/Swagger Configuration PROD
|
||||
# ============================================
|
||||
# Swagger désactivé en production par défaut (build-time property)
|
||||
# quarkus.swagger-ui.always-include=false
|
||||
quarkus.swagger-ui.enable=false
|
||||
|
||||
# Dev Services (désactivé en PROD)
|
||||
quarkus.devservices.enabled=false
|
||||
# ============================================
|
||||
# Security Configuration PROD (strict)
|
||||
# ============================================
|
||||
# Ces propriétés sont build-time, configurées dans application.properties
|
||||
# quarkus.security.auth.enabled=true
|
||||
# quarkus.security.jaxrs.deny-unannotated-endpoints=true
|
||||
# quarkus.security.auth.proactive=true
|
||||
|
||||
# Security Configuration (PROD - strict)
|
||||
quarkus.security.jaxrs.deny-unannotated-endpoints=true
|
||||
# ============================================
|
||||
# Performance tuning PROD
|
||||
# ============================================
|
||||
quarkus.thread-pool.core-threads=4
|
||||
quarkus.thread-pool.max-threads=32
|
||||
quarkus.thread-pool.queue-size=200
|
||||
|
||||
# Health Check Configuration (PROD)
|
||||
quarkus.smallrye-health.root-path=/health
|
||||
quarkus.smallrye-health.liveness-path=/health/live
|
||||
quarkus.smallrye-health.readiness-path=/health/ready
|
||||
|
||||
# Metrics Configuration (PROD)
|
||||
quarkus.micrometer.enabled=true
|
||||
quarkus.micrometer.export.prometheus.enabled=true
|
||||
quarkus.micrometer.export.prometheus.path=/metrics
|
||||
|
||||
# Jackson Configuration (PROD)
|
||||
quarkus.jackson.fail-on-unknown-properties=false
|
||||
quarkus.jackson.write-dates-as-timestamps=false
|
||||
quarkus.jackson.serialization-inclusion=non_null
|
||||
|
||||
# Performance tuning (PROD)
|
||||
quarkus.thread-pool.core-threads=2
|
||||
quarkus.thread-pool.max-threads=16
|
||||
quarkus.thread-pool.queue-size=100
|
||||
|
||||
# SSL/TLS Configuration (PROD)
|
||||
quarkus.http.ssl.certificate.key-store-file=${SSL_KEYSTORE_FILE:/etc/ssl/keystore.p12}
|
||||
quarkus.http.ssl.certificate.key-store-password=${SSL_KEYSTORE_PASSWORD}
|
||||
quarkus.http.ssl.certificate.key-store-file-type=PKCS12
|
||||
|
||||
# Monitoring & Observability
|
||||
quarkus.log.handler.gelf.enabled=false
|
||||
quarkus.log.handler.gelf.host=${GRAYLOG_HOST:logs.lions.dev}
|
||||
quarkus.log.handler.gelf.port=${GRAYLOG_PORT:12201}
|
||||
# ============================================
|
||||
# SSL/TLS Configuration PROD (optionnel)
|
||||
# ============================================
|
||||
# Décommenter si le serveur gère le SSL directement (sinon géré par Ingress/Load Balancer)
|
||||
# quarkus.http.ssl.certificate.key-store-file=${SSL_KEYSTORE_FILE:/etc/ssl/keystore.p12}
|
||||
# quarkus.http.ssl.certificate.key-store-password=${SSL_KEYSTORE_PASSWORD}
|
||||
# quarkus.http.ssl.certificate.key-store-file-type=PKCS12
|
||||
|
||||
@@ -33,8 +33,7 @@ lions.keycloak.timeout-seconds=30
|
||||
# Realms autorisés (séparés par virgule)
|
||||
lions.keycloak.authorized-realms=btpxpress,master,lions-realm
|
||||
|
||||
# Circuit Breaker Configuration
|
||||
quarkus.smallrye-fault-tolerance.enabled=true
|
||||
# Circuit Breaker Configuration (SmallRye Fault Tolerance est activé par défaut)
|
||||
|
||||
# Retry Configuration (pour appels Keycloak)
|
||||
lions.keycloak.retry.max-attempts=3
|
||||
@@ -46,14 +45,22 @@ lions.audit.log-to-database=false
|
||||
lions.audit.log-to-file=true
|
||||
lions.audit.retention-days=90
|
||||
|
||||
# Database Configuration (optionnel - pour logs d'audit)
|
||||
# Décommenter si vous voulez persister les logs d'audit en DB
|
||||
#quarkus.datasource.db-kind=postgresql
|
||||
#quarkus.datasource.username=${DB_USERNAME:audit_user}
|
||||
#quarkus.datasource.password=${DB_PASSWORD:audit_pass}
|
||||
#quarkus.datasource.jdbc.url=jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:lions_audit}
|
||||
#quarkus.hibernate-orm.database.generation=none
|
||||
#quarkus.flyway.migrate-at-start=true
|
||||
# Database Configuration (pour logs d'audit et données opérationnelles)
|
||||
# DÉSACTIVÉ - Non utilisé en production (logs gérés par Kubernetes)
|
||||
quarkus.datasource.health.enabled=false
|
||||
quarkus.datasource.devservices.enabled=false
|
||||
quarkus.datasource.db-kind=postgresql
|
||||
quarkus.datasource.username=${DB_USERNAME:lions_user}
|
||||
quarkus.datasource.password=${DB_PASSWORD:lions_password}
|
||||
quarkus.datasource.jdbc.url=jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:lions_user_manager}
|
||||
|
||||
# Hibernate ORM Configuration
|
||||
quarkus.hibernate-orm.database.generation=update
|
||||
quarkus.hibernate-orm.log.sql=false
|
||||
|
||||
# Flyway Configuration
|
||||
# DÉSACTIVÉ - Pas de base de données en production
|
||||
quarkus.flyway.migrate-at-start=false
|
||||
|
||||
# Logging Configuration
|
||||
quarkus.log.level=INFO
|
||||
@@ -64,14 +71,15 @@ quarkus.log.console.enable=true
|
||||
quarkus.log.console.format=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c{3.}] (%t) %s%e%n
|
||||
|
||||
# File Logging pour Audit
|
||||
quarkus.log.file.enable=true
|
||||
quarkus.log.file.path=logs/lions-user-manager.log
|
||||
quarkus.log.file.rotation.max-file-size=10M
|
||||
quarkus.log.file.rotation.max-backup-index=10
|
||||
# DÉSACTIVÉ - Logs gérés par Kubernetes (stdout/stderr)
|
||||
quarkus.log.file.enable=false
|
||||
# quarkus.log.file.path=logs/lions-user-manager.log
|
||||
# quarkus.log.file.rotation.max-file-size=10M
|
||||
# quarkus.log.file.rotation.max-backup-index=10
|
||||
|
||||
# OpenAPI/Swagger Configuration
|
||||
quarkus.swagger-ui.always-include=true
|
||||
quarkus.swagger-ui.path=/swagger-ui
|
||||
# Le chemin par défaut est /q/swagger-ui (pas besoin de le spécifier)
|
||||
mp.openapi.extensions.smallrye.info.title=Lions User Manager API
|
||||
mp.openapi.extensions.smallrye.info.version=1.0.0
|
||||
mp.openapi.extensions.smallrye.info.description=API de gestion centralisée des utilisateurs Keycloak
|
||||
|
||||
1
target/build-metrics.json
Normal file
1
target/build-metrics.json
Normal file
File diff suppressed because one or more lines are too long
@@ -11,11 +11,20 @@ quarkus.http.cors.methods=GET,POST,PUT,DELETE,PATCH,OPTIONS
|
||||
quarkus.http.cors.headers=*
|
||||
|
||||
# Keycloak OIDC Configuration (DEV)
|
||||
quarkus.oidc.auth-server-url=http://localhost:8180/realms/master
|
||||
quarkus.oidc.client-id=lions-user-manager
|
||||
quarkus.oidc.credentials.secret=dev-secret-change-me
|
||||
quarkus.oidc.tls.verification=none
|
||||
# Le backend vérifie les tokens JWT envoyés par le client
|
||||
# IMPORTANT: Pour un service, Quarkus valide les tokens JWT sans avoir besoin d'un client-id/secret
|
||||
# Le backend accepte les tokens émis pour n'importe quel client du realm
|
||||
quarkus.oidc.enabled=true
|
||||
quarkus.oidc.auth-server-url=http://localhost:8180/realms/lions-user-manager
|
||||
quarkus.oidc.application-type=service
|
||||
quarkus.oidc.tls.verification=none
|
||||
quarkus.oidc.token.issuer=http://localhost:8180/realms/lions-user-manager
|
||||
quarkus.oidc.discovery-enabled=true
|
||||
# Accepter les tokens avec audience "account" (audience par défaut de Keycloak)
|
||||
# Cela permet d'accepter les tokens émis pour le frontend sans configuration Keycloak supplémentaire
|
||||
quarkus.oidc.token.audience=account
|
||||
# Vérifier le token (obligatoire pour un service)
|
||||
quarkus.oidc.verify-access-token=true
|
||||
|
||||
# Keycloak Admin Client Configuration (DEV)
|
||||
lions.keycloak.server-url=http://localhost:8180
|
||||
@@ -27,7 +36,7 @@ lions.keycloak.connection-pool-size=5
|
||||
lions.keycloak.timeout-seconds=30
|
||||
|
||||
# Realms autorisés (DEV)
|
||||
lions.keycloak.authorized-realms=btpxpress,master,lions-realm,test-realm
|
||||
lions.keycloak.authorized-realms=lions-user-manager,master,btpxpress,test-realm
|
||||
|
||||
# Circuit Breaker Configuration (DEV - plus permissif)
|
||||
quarkus.smallrye-fault-tolerance.enabled=true
|
||||
@@ -52,14 +61,17 @@ lions.audit.retention-days=30
|
||||
#quarkus.flyway.migrate-at-start=false
|
||||
|
||||
# Logging Configuration (DEV)
|
||||
quarkus.log.level=DEBUG
|
||||
quarkus.log.level=INFO
|
||||
quarkus.log.category."dev.lions.user.manager".level=DEBUG
|
||||
quarkus.log.category."org.keycloak".level=INFO
|
||||
quarkus.log.category."io.quarkus".level=INFO
|
||||
# Logging OIDC pour debug
|
||||
quarkus.log.category."io.quarkus.oidc".level=DEBUG
|
||||
quarkus.log.category."io.quarkus.security".level=DEBUG
|
||||
|
||||
quarkus.log.console.enable=true
|
||||
quarkus.log.console.format=%d{HH:mm:ss} %-5p [%c{2.}] (%t) %s%e%n
|
||||
quarkus.log.console.color=true
|
||||
# quarkus.log.console.color est déprécié dans Quarkus 3.x
|
||||
|
||||
# File Logging pour Audit (DEV)
|
||||
quarkus.log.file.enable=true
|
||||
@@ -69,14 +81,48 @@ quarkus.log.file.rotation.max-backup-index=3
|
||||
|
||||
# OpenAPI/Swagger Configuration (DEV - toujours activé)
|
||||
quarkus.swagger-ui.always-include=true
|
||||
quarkus.swagger-ui.path=/swagger-ui
|
||||
quarkus.swagger-ui.enable=true
|
||||
# Le chemin par défaut est /q/swagger-ui (pas besoin de le spécifier)
|
||||
|
||||
# Dev Services (activé en DEV)
|
||||
quarkus.devservices.enabled=false
|
||||
|
||||
# Security Configuration (DEV - plus permissif)
|
||||
# Security Configuration (DEV)
|
||||
# La sécurité est activée - les rôles sont vérifiés via OIDC/Keycloak
|
||||
# Note: KeycloakTestUserConfig configure automatiquement l'utilisateur de test au démarrage
|
||||
quarkus.security.auth.enabled=true
|
||||
quarkus.security.jaxrs.deny-unannotated-endpoints=false
|
||||
quarkus.security.auth.proactive=false
|
||||
|
||||
# Configuration OIDC - Extraction des rôles
|
||||
# Le backend extrait les rôles depuis realm_access/roles (standard Keycloak)
|
||||
# Le scope "roles" de Keycloak crée automatiquement realm_access.roles
|
||||
# Syntaxe Quarkus: utiliser un slash pour les chemins imbriqués
|
||||
quarkus.oidc.roles.role-claim-path=realm_access/roles
|
||||
|
||||
# Définir explicitement le profil pour que DevSecurityContextProducer le détecte
|
||||
quarkus.profile=dev
|
||||
|
||||
# Logging pour debug du filtre de sécurité
|
||||
quarkus.log.category."dev.lions.user.manager.security".level=DEBUG
|
||||
|
||||
# Logging OIDC et Security pour debug
|
||||
quarkus.log.category."io.quarkus.oidc".level=DEBUG
|
||||
quarkus.log.category."io.quarkus.oidc.runtime".level=DEBUG
|
||||
quarkus.log.category."io.quarkus.security".level=DEBUG
|
||||
quarkus.log.category."io.quarkus.security.runtime".level=DEBUG
|
||||
|
||||
# Hot Reload
|
||||
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
|
||||
|
||||
@@ -71,7 +71,7 @@ quarkus.log.file.rotation.max-backup-index=10
|
||||
|
||||
# OpenAPI/Swagger Configuration
|
||||
quarkus.swagger-ui.always-include=true
|
||||
quarkus.swagger-ui.path=/swagger-ui
|
||||
# Le chemin par défaut est /q/swagger-ui (pas besoin de le spécifier)
|
||||
mp.openapi.extensions.smallrye.info.title=Lions User Manager API
|
||||
mp.openapi.extensions.smallrye.info.version=1.0.0
|
||||
mp.openapi.extensions.smallrye.info.description=API de gestion centralisée des utilisateurs Keycloak
|
||||
|
||||
Binary file not shown.
BIN
target/classes/dev/lions/user/manager/config/JacksonConfig.class
Normal file
BIN
target/classes/dev/lions/user/manager/config/JacksonConfig.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
target/lions-user-manager-server-impl-quarkus-dev.jar
Normal file
BIN
target/lions-user-manager-server-impl-quarkus-dev.jar
Normal file
Binary file not shown.
@@ -1,3 +0,0 @@
|
||||
artifactId=lions-user-manager-server-impl-quarkus
|
||||
groupId=dev.lions.user.manager
|
||||
version=1.0.0
|
||||
@@ -1,10 +1,31 @@
|
||||
dev\lions\user\manager\resource\UserResource$ErrorResponse.class
|
||||
dev\lions\user\manager\mapper\RoleMapper.class
|
||||
dev\lions\user\manager\resource\AuditResource$ErrorResponse.class
|
||||
dev\lions\user\manager\resource\SyncResource$SyncUsersResponse.class
|
||||
dev\lions\user\manager\service\impl\UserServiceImpl.class
|
||||
dev\lions\user\manager\client\KeycloakAdminClient.class
|
||||
dev\lions\user\manager\client\KeycloakAdminClientImpl.class
|
||||
dev\lions\user\manager\resource\UserResource.class
|
||||
dev\lions\user\manager\resource\SyncResource$ErrorResponse.class
|
||||
dev\lions\user\manager\service\impl\RoleServiceImpl.class
|
||||
dev\lions\user\manager\security\DevSecurityContextProducer$DevSecurityContext$1.class
|
||||
dev\lions\user\manager\resource\AuditResource$CountResponse.class
|
||||
dev\lions\user\manager\resource\KeycloakHealthCheck.class
|
||||
dev\lions\user\manager\resource\UserResource$SessionsRevokedResponse.class
|
||||
dev\lions\user\manager\resource\AuditResource.class
|
||||
dev\lions\user\manager\resource\SyncResource$SyncRolesResponse.class
|
||||
dev\lions\user\manager\mapper\UserMapper.class
|
||||
dev\lions\user\manager\resource\HealthResourceEndpoint.class
|
||||
dev\lions\user\manager\resource\RoleResource$RoleAssignmentRequest.class
|
||||
dev\lions\user\manager\resource\UserResource$PasswordResetRequest.class
|
||||
dev\lions\user\manager\security\DevSecurityContextProducer$DevSecurityContext.class
|
||||
dev\lions\user\manager\config\KeycloakTestUserConfig.class
|
||||
dev\lions\user\manager\service\impl\SyncServiceImpl.class
|
||||
dev\lions\user\manager\security\DevSecurityContextProducer.class
|
||||
dev\lions\user\manager\resource\UserResource$ErrorResponse.class
|
||||
dev\lions\user\manager\service\impl\AuditServiceImpl.class
|
||||
dev\lions\user\manager\resource\SyncResource.class
|
||||
dev\lions\user\manager\client\KeycloakAdminClient.class
|
||||
dev\lions\user\manager\resource\RoleResource.class
|
||||
dev\lions\user\manager\config\JacksonConfig.class
|
||||
dev\lions\user\manager\resource\SyncResource$HealthCheckResponse.class
|
||||
dev\lions\user\manager\client\KeycloakAdminClientImpl.class
|
||||
dev\lions\user\manager\resource\RoleResource$ErrorResponse.class
|
||||
dev\lions\user\manager\resource\UserResource.class
|
||||
dev\lions\user\manager\resource\SyncResource$ExistsCheckResponse.class
|
||||
dev\lions\user\manager\resource\UserResource$SessionsRevokedResponse.class
|
||||
dev\lions\user\manager\resource\HealthResourceEndpoint.class
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-impl-quarkus\src\main\java\dev\lions\user\manager\resource\KeycloakHealthCheck.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-impl-quarkus\src\main\java\dev\lions\user\manager\service\impl\UserServiceImpl.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-impl-quarkus\src\main\java\dev\lions\user\manager\resource\AuditResource.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-impl-quarkus\src\main\java\dev\lions\user\manager\resource\HealthResourceEndpoint.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-impl-quarkus\src\main\java\dev\lions\user\manager\mapper\RoleMapper.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-impl-quarkus\src\main\java\dev\lions\user\manager\resource\SyncResource.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-impl-quarkus\src\main\java\dev\lions\user\manager\resource\UserResource.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-impl-quarkus\src\main\java\dev\lions\user\manager\client\KeycloakAdminClient.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-impl-quarkus\src\main\java\dev\lions\user\manager\mapper\UserMapper.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-impl-quarkus\src\main\java\dev\lions\user\manager\config\KeycloakTestUserConfig.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-impl-quarkus\src\main\java\dev\lions\user\manager\resource\KeycloakHealthCheck.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-impl-quarkus\src\main\java\dev\lions\user\manager\service\impl\UserServiceImpl.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-impl-quarkus\src\main\java\dev\lions\user\manager\security\DevSecurityContextProducer.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-impl-quarkus\src\main\java\dev\lions\user\manager\service\impl\AuditServiceImpl.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-impl-quarkus\src\main\java\dev\lions\user\manager\service\impl\SyncServiceImpl.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-impl-quarkus\src\main\java\dev\lions\user\manager\resource\RoleResource.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-impl-quarkus\src\main\java\dev\lions\user\manager\client\KeycloakAdminClientImpl.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-impl-quarkus\src\main\java\dev\lions\user\manager\config\JacksonConfig.java
|
||||
C:\Users\dadyo\PersonalProjects\lions-workspace\lions-user-manager\lions-user-manager-server-impl-quarkus\src\main\java\dev\lions\user\manager\service\impl\RoleServiceImpl.java
|
||||
|
||||
BIN
target/quarkus/bootstrap/dev-app-model.dat
Normal file
BIN
target/quarkus/bootstrap/dev-app-model.dat
Normal file
Binary file not shown.
Reference in New Issue
Block a user