From 97a3e13bc6c17b3e898b1e6b0120be312e32c17c Mon Sep 17 00:00:00 2001
From: Alex Morel <amorel@codelutin.com>
Date: Fri, 19 Jul 2024 10:12:45 +0200
Subject: [PATCH] Add option to activate full SCIM requests logs

---
 ...intConfigurationStorageProviderFactory.java | 11 ++++++++---
 .../scim/core/ScrimEndPointConfiguration.java  |  7 +++++++
 .../sh/libre/scim/core/service/ScimClient.java | 18 ++++++++++++++++--
 3 files changed, 31 insertions(+), 5 deletions(-)

diff --git a/src/main/java/sh/libre/scim/core/ScimEndpointConfigurationStorageProviderFactory.java b/src/main/java/sh/libre/scim/core/ScimEndpointConfigurationStorageProviderFactory.java
index 60f61b8..1197980 100644
--- a/src/main/java/sh/libre/scim/core/ScimEndpointConfigurationStorageProviderFactory.java
+++ b/src/main/java/sh/libre/scim/core/ScimEndpointConfigurationStorageProviderFactory.java
@@ -39,16 +39,17 @@ public class ScimEndpointConfigurationStorageProviderFactory
     public SynchronizationResult sync(KeycloakSessionFactory sessionFactory, String realmId,
                                       UserStorageProviderModel model) {
         // Manually Launch a synchronization between keycloack and the SCIM endpoint described in the given model
-        LOGGER.infof("[SCIM] Sync from ScimStorageProvider - Realm %s - Model %s", realmId, model.getId());
+        LOGGER.infof("[SCIM] Sync from ScimStorageProvider - Realm %s - Model %s", realmId, model.getName());
         SynchronizationResult result = new SynchronizationResult();
         KeycloakModelUtils.runJobInTransaction(sessionFactory, session -> {
             RealmModel realm = session.realms().getRealm(realmId);
             session.getContext().setRealm(realm);
             ScimDispatcher dispatcher = new ScimDispatcher(session);
-            if (BooleanUtils.TRUE.equals(model.get("propagation-user"))) {
+            LOGGER.info("-->" + model.get(ScrimEndPointConfiguration.CONF_KEY_PROPAGATION_USER) + "//" + model.get(ScrimEndPointConfiguration.CONF_KEY_PROPAGATION_GROUP));
+            if (BooleanUtils.TRUE.equals(model.get(ScrimEndPointConfiguration.CONF_KEY_PROPAGATION_USER))) {
                 dispatcher.dispatchUserModificationToOne(model, client -> client.sync(result));
             }
-            if (BooleanUtils.TRUE.equals(model.get("propagation-group"))) {
+            if (BooleanUtils.TRUE.equals(model.get(ScrimEndPointConfiguration.CONF_KEY_PROPAGATION_GROUP))) {
                 dispatcher.dispatchGroupModificationToOne(model, client -> client.sync(result));
             }
             dispatcher.close();
@@ -139,6 +140,10 @@ public class ScimEndpointConfigurationStorageProviderFactory
                 .name(ScrimEndPointConfiguration.CONF_KEY_SYNC_REFRESH)
                 .type(ProviderConfigProperty.BOOLEAN_TYPE)
                 .label("Enable refresh during sync")
+                .name(ScrimEndPointConfiguration.CONF_KEY_LOG_ALL_SCIM_REQUESTS)
+                .type(ProviderConfigProperty.BOOLEAN_TYPE)
+                .label("Log SCIM requests and responses")
+                .helpText("If true, all sent SCIM requests and responses will be logged")
                 .add()
                 .build();
     }
diff --git a/src/main/java/sh/libre/scim/core/ScrimEndPointConfiguration.java b/src/main/java/sh/libre/scim/core/ScrimEndPointConfiguration.java
index 23755fd..6359b57 100644
--- a/src/main/java/sh/libre/scim/core/ScrimEndPointConfiguration.java
+++ b/src/main/java/sh/libre/scim/core/ScrimEndPointConfiguration.java
@@ -15,6 +15,7 @@ public class ScrimEndPointConfiguration {
     public static final String CONF_KEY_SYNC_REFRESH = "sync-refresh";
     public static final String CONF_KEY_PROPAGATION_USER = "propagation-user";
     public static final String CONF_KEY_PROPAGATION_GROUP = "propagation-group";
+    public static final String CONF_KEY_LOG_ALL_SCIM_REQUESTS = "log-all-scim-requests";
 
     private final String endPoint;
     private final String id;
@@ -24,6 +25,7 @@ public class ScrimEndPointConfiguration {
     private final ImportAction importAction;
     private final boolean pullFromScimSynchronisationActivated;
     private final boolean pushToScimSynchronisationActivated;
+    private final boolean logAllScimRequests;
 
     public ScrimEndPointConfiguration(ComponentModel scimProviderConfiguration) {
         try {
@@ -47,6 +49,7 @@ public class ScrimEndPointConfiguration {
             importAction = ImportAction.valueOf(scimProviderConfiguration.get(CONF_KEY_SYNC_IMPORT_ACTION));
             pullFromScimSynchronisationActivated = scimProviderConfiguration.get(CONF_KEY_SYNC_IMPORT, false);
             pushToScimSynchronisationActivated = scimProviderConfiguration.get(CONF_KEY_SYNC_REFRESH, false);
+            logAllScimRequests = scimProviderConfiguration.get(CONF_KEY_LOG_ALL_SCIM_REQUESTS, false);
         } catch (IllegalArgumentException e) {
             throw new IllegalArgumentException("authMode '" + scimProviderConfiguration.get(CONF_KEY_AUTH_MODE) + "' is not supported");
         }
@@ -84,6 +87,10 @@ public class ScrimEndPointConfiguration {
         return endPoint;
     }
 
+    public boolean isLogAllScimRequests() {
+        return logAllScimRequests;
+    }
+
     public enum AuthMode {
         BEARER, BASIC_AUTH, NONE
     }
diff --git a/src/main/java/sh/libre/scim/core/service/ScimClient.java b/src/main/java/sh/libre/scim/core/service/ScimClient.java
index 90bcfcf..8a5cd73 100644
--- a/src/main/java/sh/libre/scim/core/service/ScimClient.java
+++ b/src/main/java/sh/libre/scim/core/service/ScimClient.java
@@ -28,8 +28,9 @@ public class ScimClient<S extends ResourceNode> implements AutoCloseable {
     private final ScimRequestBuilder scimRequestBuilder;
 
     private final ScimResourceType scimResourceType;
+    private final boolean logAllRequests;
 
-    private ScimClient(ScimRequestBuilder scimRequestBuilder, ScimResourceType scimResourceType) {
+    private ScimClient(ScimRequestBuilder scimRequestBuilder, ScimResourceType scimResourceType, boolean detailedLogs) {
         this.scimRequestBuilder = scimRequestBuilder;
         this.scimResourceType = scimResourceType;
         RetryConfig retryConfig = RetryConfig.custom()
@@ -38,6 +39,7 @@ public class ScimClient<S extends ResourceNode> implements AutoCloseable {
                 .retryExceptions(ProcessingException.class)
                 .build();
         retryRegistry = RetryRegistry.of(retryConfig);
+        this.logAllRequests = detailedLogs;
     }
 
     public static ScimClient open(ScrimEndPointConfiguration scimProviderConfiguration, ScimResourceType scimResourceType) {
@@ -56,7 +58,7 @@ public class ScimClient<S extends ResourceNode> implements AutoCloseable {
                         scimApplicationBaseUrl,
                         scimClientConfig
                 );
-        return new ScimClient(scimRequestBuilder, scimResourceType);
+        return new ScimClient(scimRequestBuilder, scimResourceType, scimProviderConfiguration.isLogAllScimRequests());
     }
 
     public EntityOnRemoteScimId create(KeycloakId id, S scimForCreation) throws InvalidResponseFromScimEndpointException {
@@ -68,6 +70,9 @@ public class ScimClient<S extends ResourceNode> implements AutoCloseable {
         }
         try {
             Retry retry = retryRegistry.retry("create-%s".formatted(id.asString()));
+            if (logAllRequests) {
+                LOGGER.warn("[SCIM] Sending " + scimForCreation.toPrettyString() + "\n to " + getScimEndpoint());
+            }
             ServerResponse<S> response = retry.executeSupplier(() -> scimRequestBuilder
                     .create(getResourceClass(), getScimEndpoint())
                     .setResource(scimForCreation)
@@ -86,6 +91,9 @@ public class ScimClient<S extends ResourceNode> implements AutoCloseable {
     }
 
     private void checkResponseIsSuccess(ServerResponse<S> response) throws InvalidResponseFromScimEndpointException {
+        if (logAllRequests) {
+            LOGGER.warn("[SCIM] Server response " + response.getHttpStatus() + "\n" + response.getResponseBody());
+        }
         if (!response.isSuccess()) {
             throw new InvalidResponseFromScimEndpointException(response, "Server answered with status " + response.getResponseBody() + ": " + response.getResponseBody());
         }
@@ -102,6 +110,9 @@ public class ScimClient<S extends ResourceNode> implements AutoCloseable {
     public void update(EntityOnRemoteScimId externalId, S scimForReplace) throws InvalidResponseFromScimEndpointException {
         Retry retry = retryRegistry.retry("replace-%s".formatted(externalId.asString()));
         try {
+            if (logAllRequests) {
+                LOGGER.warn("[SCIM] Sending Update " + scimForReplace.toPrettyString() + "\n to " + getScimEndpoint());
+            }
             ServerResponse<S> response = retry.executeSupplier(() -> scimRequestBuilder
                     .update(getResourceClass(), getScimEndpoint(), externalId.asString())
                     .setResource(scimForReplace)
@@ -116,6 +127,9 @@ public class ScimClient<S extends ResourceNode> implements AutoCloseable {
 
     public void delete(EntityOnRemoteScimId externalId) throws InvalidResponseFromScimEndpointException {
         Retry retry = retryRegistry.retry("delete-%s".formatted(externalId.asString()));
+        if (logAllRequests) {
+            LOGGER.warn("[SCIM] Sending DELETE to " + getScimEndpoint());
+        }
         try {
             ServerResponse<S> response = retry.executeSupplier(() -> scimRequestBuilder
                     .delete(getResourceClass(), getScimEndpoint(), externalId.asString())
-- 
GitLab