fix(security): audit RBAC complet v3.0 — rôles normalisés, lifecycle, changement mdp mobile
RBAC:
- HealthResource: @PermitAll
- RoleResource: @RolesAllowed ADMIN/SUPER_ADMIN/ADMIN_ORGANISATION class-level
- PropositionAideResource: @RolesAllowed MEMBRE/USER class-level
- AuthCallbackResource: @PermitAll
- EvenementResource: @PermitAll /publics et /test, count restreint
- BackupResource/LogsMonitoringResource/SystemResource: MODERATOR → MODERATEUR
- AnalyticsResource: MANAGER/MEMBER → ADMIN_ORGANISATION/MEMBRE
- RoleConstant.java: constantes de rôles centralisées
Cycle de vie membres:
- MemberLifecycleService: ajouterMembre()/retirerMembre() sur activation/radiation/archivage
- MembreResource: endpoint GET /numero/{numeroMembre}
- MembreService: méthode trouverParNumeroMembre()
Changement mot de passe:
- CompteAdherentResource: endpoint POST /auth/change-password (mobile)
- MembreKeycloakSyncService: changerMotDePasseDirectKeycloak() via API Admin Keycloak directe
- Fallback automatique si lions-user-manager indisponible
Workflow:
- Flyway V17-V23: rôles, types org, formules Option C, lifecycle columns, bareme cotisation
- Nouvelles classes: MemberLifecycleService, OrganisationModuleService, scheduler
- Security: OrganisationContextFilter, OrganisationContextHolder, ModuleAccessFilter
This commit is contained in:
@@ -149,6 +149,62 @@ public class WaveCheckoutService {
|
||||
return HexFormat.of().formatHex(hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interroge l'état d'une session Wave Checkout (spec : GET /v1/checkout/sessions/:id).
|
||||
* Utilisé par le polling web pour détecter automatiquement la complétion du paiement.
|
||||
*
|
||||
* @param sessionId ID de session Wave (cos-xxx)
|
||||
* @return statut de la session (checkout_status, payment_status, transaction_id)
|
||||
*/
|
||||
public WaveSessionStatusResponse getSession(String sessionId) throws WaveCheckoutException {
|
||||
boolean useMock = mockEnabled || apiKey == null || apiKey.trim().isBlank();
|
||||
if (useMock) {
|
||||
// En mock, on ne peut pas vraiment vérifier — retourner EN_COURS (polling s'arrête via /web-success)
|
||||
LOG.warnf("Wave getSession en mode MOCK — session %s", sessionId);
|
||||
return new WaveSessionStatusResponse(sessionId, "open", "processing", null);
|
||||
}
|
||||
|
||||
String base = (baseUrl == null || baseUrl.endsWith("/")) ? baseUrl.replaceAll("/+$", "") : baseUrl;
|
||||
if (!base.endsWith("/v1")) base = base + "/v1";
|
||||
String url = base + "/checkout/sessions/" + sessionId;
|
||||
|
||||
try {
|
||||
long timestamp = System.currentTimeMillis() / 1000;
|
||||
java.net.http.HttpRequest.Builder requestBuilder = java.net.http.HttpRequest.newBuilder()
|
||||
.uri(URI.create(url))
|
||||
.header("Authorization", "Bearer " + apiKey)
|
||||
.header("Content-Type", "application/json")
|
||||
.timeout(Duration.ofSeconds(15))
|
||||
.GET();
|
||||
|
||||
if (signingSecret != null && !signingSecret.trim().isBlank()) {
|
||||
String sig = computeWaveSignature(timestamp, "");
|
||||
requestBuilder.header("Wave-Signature", "t=" + timestamp + ",v1=" + sig);
|
||||
}
|
||||
|
||||
java.net.http.HttpClient client = java.net.http.HttpClient.newBuilder().build();
|
||||
java.net.http.HttpResponse<String> response = client.send(
|
||||
requestBuilder.build(),
|
||||
java.net.http.HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
||||
|
||||
if (response.statusCode() >= 400) {
|
||||
throw new WaveCheckoutException("Wave API: " + response.statusCode() + " " + response.body());
|
||||
}
|
||||
|
||||
JsonNode root = objectMapper.readTree(response.body());
|
||||
String checkoutStatus = root.has("checkout_status") ? root.get("checkout_status").asText() : null;
|
||||
String paymentStatus = root.has("payment_status") ? root.get("payment_status").asText() : null;
|
||||
String transactionId = root.has("transaction_id") ? root.get("transaction_id").asText() : null;
|
||||
return new WaveSessionStatusResponse(sessionId, checkoutStatus, paymentStatus, transactionId);
|
||||
|
||||
} catch (WaveCheckoutException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
throw new WaveCheckoutException("Erreur vérification session Wave: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getRedirectBaseUrl() {
|
||||
return (redirectBaseUrl == null || redirectBaseUrl.trim().isBlank()) ? "http://localhost:8080" : redirectBaseUrl.trim();
|
||||
}
|
||||
@@ -159,6 +215,31 @@ public class WaveCheckoutService {
|
||||
return new WaveCheckoutSessionResponse(mockId, successUrl);
|
||||
}
|
||||
|
||||
public static final class WaveSessionStatusResponse {
|
||||
public final String sessionId;
|
||||
/** "open" | "complete" | "expired" */
|
||||
public final String checkoutStatus;
|
||||
/** "processing" | "cancelled" | "succeeded" */
|
||||
public final String paymentStatus;
|
||||
/** ID transaction Wave (TCN...) — non-null si succeeded */
|
||||
public final String transactionId;
|
||||
|
||||
public WaveSessionStatusResponse(String sessionId, String checkoutStatus, String paymentStatus, String transactionId) {
|
||||
this.sessionId = sessionId;
|
||||
this.checkoutStatus = checkoutStatus;
|
||||
this.paymentStatus = paymentStatus;
|
||||
this.transactionId = transactionId;
|
||||
}
|
||||
|
||||
public boolean isSucceeded() {
|
||||
return "succeeded".equals(paymentStatus) && "complete".equals(checkoutStatus);
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
return "expired".equals(checkoutStatus);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class WaveCheckoutSessionResponse {
|
||||
public final String id;
|
||||
public final String waveLaunchUrl;
|
||||
|
||||
Reference in New Issue
Block a user