This is an automated email from the ASF dual-hosted git repository.

ilgrosso pushed a commit to branch 4_0_X
in repository https://gitbox.apache.org/repos/asf/syncope.git

commit 9e04a6516ece82d7020bf5f816c40d1eaf029e99
Author: Francesco Chicchiriccò <[email protected]>
AuthorDate: Wed Jan 28 13:40:19 2026 +0100

    [SYNCOPE-1946] Adding audit history features to Realm page
---
 .../client/console/audit/AuditHistoryDetails.java  |  7 ++-
 .../syncope/client/console/pages/Realms.java       | 56 +++++++++++++++++++---
 .../syncope/client/console/panels/Realm.java       | 14 ++++++
 .../syncope/client/console/pages/Realms.properties |  1 +
 .../client/console/pages/Realms_fr_CA.properties   |  3 +-
 .../client/console/pages/Realms_it.properties      |  1 +
 .../client/console/pages/Realms_ja.properties      |  1 +
 .../client/console/pages/Realms_pt_BR.properties   |  1 +
 .../client/console/pages/Realms_ru.properties      |  1 +
 .../core/persistence/jpa/dao/JPAAuditEventDAO.java |  8 ++--
 .../dao/ElasticsearchAuditEventDAO.java            |  2 +-
 .../opensearch/dao/OpenSearchAuditEventDAO.java    |  2 +-
 12 files changed, 81 insertions(+), 16 deletions(-)

diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDetails.java
 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDetails.java
index eed5a3a622..b44503fbf9 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDetails.java
+++ 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/audit/AuditHistoryDetails.java
@@ -330,17 +330,20 @@ public abstract class AuditHistoryDetails<T extends 
Serializable> extends Panel
         }
 
         try {
-            String content;
+            String content = null;
             if (auditEvent.getBefore() == null) {
                 JsonNode output = MAPPER.readTree(auditEvent.getOutput());
                 if (output.has("entity")) {
                     content = output.get("entity").toPrettyString();
-                } else {
+                } else if ((!output.has("content") && 
output.get("content").isArray())) {
                     content = output.toPrettyString();
                 }
             } else {
                 content = auditEvent.getBefore();
             }
+            if (content == null || "null".equals(content)) {
+                return Model.of();
+            }
 
             T entity = MAPPER.reader().
                     with(StreamReadFeature.STRICT_DUPLICATE_DETECTION).
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Realms.java
 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Realms.java
index 7839d160b9..61bcb41eed 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Realms.java
+++ 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/pages/Realms.java
@@ -18,15 +18,18 @@
  */
 package org.apache.syncope.client.console.pages;
 
+import com.fasterxml.jackson.databind.json.JsonMapper;
 import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
 import java.io.Serializable;
 import java.util.List;
 import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.audit.AuditHistoryModal;
 import org.apache.syncope.client.console.panels.Realm;
 import org.apache.syncope.client.console.panels.RealmChoicePanel;
 import org.apache.syncope.client.console.panels.RealmChoicePanel.ChosenRealm;
 import org.apache.syncope.client.console.rest.AnyTypeRestClient;
+import org.apache.syncope.client.console.rest.AuditRestClient;
 import org.apache.syncope.client.console.rest.RealmRestClient;
 import org.apache.syncope.client.console.tasks.TemplatesTogglePanel;
 import 
org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
@@ -40,6 +43,8 @@ import org.apache.syncope.common.lib.to.AnyTypeTO;
 import org.apache.syncope.common.lib.to.ProvisioningResult;
 import org.apache.syncope.common.lib.to.RealmTO;
 import org.apache.syncope.common.lib.to.TemplatableTO;
+import org.apache.syncope.common.lib.types.IdRepoEntitlement;
+import org.apache.syncope.common.lib.types.OpEvent;
 import org.apache.wicket.PageReference;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.event.Broadcast;
@@ -56,6 +61,8 @@ public class Realms extends BasePage {
 
     private static final long serialVersionUID = -1100228004207271270L;
 
+    protected static final JsonMapper MAPPER = 
JsonMapper.builder().findAndAddModules().build();
+
     public static final String SELECTED_INDEX = "selectedIndex";
 
     public static final String INITIAL_REALM = "initialRealm";
@@ -66,6 +73,9 @@ public class Realms extends BasePage {
     @SpringBean
     protected AnyTypeRestClient anyTypeRestClient;
 
+    @SpringBean
+    protected AuditRestClient auditRestClient;
+
     protected final TemplatesTogglePanel templates;
 
     protected final RealmChoicePanel realmChoicePanel;
@@ -119,7 +129,7 @@ public class Realms extends BasePage {
                 setFooterVisible(false);
             }
         };
-        templateModal.size(Modal.Size.Large);
+        templateModal.size(Modal.Size.Extra_large);
         content.add(templateModal);
 
         modal.setWindowClosedCallback(target -> {
@@ -191,12 +201,6 @@ public class Realms extends BasePage {
             super("body", realmTO, anyTypes, selectedIndex, 
Realms.this.getPageReference());
         }
 
-        @Override
-        protected void onClickTemplate(final AjaxRequestTarget target) {
-            templates.setTargetObject(realmTO);
-            templates.toggle(target, true);
-        }
-
         @Override
         protected void setWindowClosedReloadCallback(final BaseModal<?> modal) 
{
             modal.setWindowClosedCallback(target -> {
@@ -214,6 +218,12 @@ public class Realms extends BasePage {
             });
         }
 
+        @Override
+        protected void onClickTemplate(final AjaxRequestTarget target) {
+            templates.setTargetObject(realmTO);
+            templates.toggle(target, true);
+        }
+
         @Override
         protected void onClickCreate(final AjaxRequestTarget target) {
             this.wizardBuilder.setParent(realmChoicePanel.getCurrentRealm());
@@ -237,6 +247,38 @@ public class Realms extends BasePage {
             });
         }
 
+        @Override
+        protected void onClickAudit(final AjaxRequestTarget target, final 
RealmTO realmTO) {
+            target.add(templateModal.setContent(new AuditHistoryModal<>(
+                    OpEvent.CategoryType.LOGIC,
+                    "RealmLogic",
+                    realmTO,
+                    IdRepoEntitlement.REALM_UPDATE,
+                    auditRestClient) {
+
+                private static final long serialVersionUID = 
-5819724478921691835L;
+
+                @Override
+                protected void restore(final String json, final 
AjaxRequestTarget target) {
+                    try {
+                        RealmTO updated = MAPPER.readValue(json, 
RealmTO.class);
+                        realmRestClient.update(updated);
+
+                        
SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
+                        target.add(realmChoicePanel.reloadRealmTree(target));
+                    } catch (Exception e) {
+                        LOG.error("While restoring realm {}", 
realmTO.getKey(), e);
+                        SyncopeConsoleSession.get().onException(e);
+                    }
+                    ((BasePage) 
pageRef.getPage()).getNotificationPanel().refresh(target);
+                }
+            }));
+
+            templateModal.header(new 
Model<>(getString("realm.auditHistory.title", new Model<>(realmTO))));
+
+            templateModal.show(true);
+        }
+
         @Override
         protected void onClickDelete(final AjaxRequestTarget target, final 
RealmTO realmTO) {
             try {
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/Realm.java
 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/Realm.java
index 3dace52aa6..a27a4c2f7d 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/Realm.java
+++ 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/Realm.java
@@ -234,6 +234,8 @@ public abstract class Realm extends WizardMgtPanel<RealmTO> 
{
 
     protected abstract void onClickEdit(AjaxRequestTarget target, RealmTO 
realmTO);
 
+    protected abstract void onClickAudit(AjaxRequestTarget target, RealmTO 
realmTO);
+
     protected abstract void onClickDelete(AjaxRequestTarget target, RealmTO 
realmTO);
 
     protected static class RemoteRealmPanel extends RemoteObjectPanel {
@@ -306,6 +308,18 @@ public abstract class Realm extends 
WizardMgtPanel<RealmTO> {
                 }, ActionLink.ActionType.TEMPLATE, 
IdRepoEntitlement.REALM_UPDATE).hideLabel();
             }
 
+            if (securityCheck(Set.of(IdRepoEntitlement.AUDIT_LIST))) {
+                actionPanel.add(new ActionLink<>(realmTO) {
+
+                    private static final long serialVersionUID = 
2802988981431379827L;
+
+                    @Override
+                    public void onClick(final AjaxRequestTarget target, final 
RealmTO ignore) {
+                        onClickAudit(target, realmTO);
+                    }
+                }, ActionLink.ActionType.VIEW_AUDIT_HISTORY, 
IdRepoEntitlement.AUDIT_LIST).hideLabel();
+            }
+
             if (securityCheck(Set.of(IdRepoEntitlement.REALM_DELETE))) {
                 actionPanel.add(new ActionLink<>(realmTO) {
 
diff --git 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms.properties
 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms.properties
index 4cfe42dbe8..7aaf70ef35 100644
--- 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms.properties
+++ 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms.properties
@@ -22,3 +22,4 @@ realmLabel=Realm
 dynRealmLabel=Dynamic Realm
 realms=Realms
 dynrealms=Dynamic Realms
+realm.auditHistory.title=Realm ${fullPath} history
diff --git 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms_fr_CA.properties
 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms_fr_CA.properties
index d6d208e376..9a980e0093 100644
--- 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms_fr_CA.properties
+++ 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms_fr_CA.properties
@@ -16,9 +16,10 @@
 # under the License.
 any.realm.new=Nouveau domaine
 any.realm.edit=Modifier domaine ${fullPath}
-inner.template.edit=Modifier mod�le ${gauche} pour '${right.fullPath}''.
+inner.template.edit=Modifier mod\u00e8le ${gauche} pour '${right.fullPath}''.
 afterObj=Lien d'objet
 realmLabel=Domaine
 dynRealmLabel=Domaine dynamique
 realms=Domaines
 dynrealms=Domaines dynamiques
+realm.auditHistory.title=Realm ${fullPath} histoire
diff --git 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms_it.properties
 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms_it.properties
index c2f149c586..135fd9aafc 100644
--- 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms_it.properties
+++ 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms_it.properties
@@ -22,3 +22,4 @@ realmLabel=Realm
 dynRealmLabel=Realm Dinamico
 realms=Realm
 dynrealms=Realm Dinamici
+realm.auditHistory.title=Realm ${fullPath} storico
diff --git 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms_ja.properties
 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms_ja.properties
index 3c02474b31..1c31ef35b2 100644
--- 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms_ja.properties
+++ 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms_ja.properties
@@ -22,3 +22,4 @@ realmLabel=\u30ec\u30eb\u30e0
 dynRealmLabel=\u52d5\u7684\u30ec\u30eb\u30e0
 realms=\u30ec\u30eb\u30e0
 dynrealms=\u52d5\u7684\u30ec\u30eb\u30e0
+realm.auditHistory.title=Realm ${fullPath} history
diff --git 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms_pt_BR.properties
 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms_pt_BR.properties
index 4cfe42dbe8..7aaf70ef35 100644
--- 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms_pt_BR.properties
+++ 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms_pt_BR.properties
@@ -22,3 +22,4 @@ realmLabel=Realm
 dynRealmLabel=Dynamic Realm
 realms=Realms
 dynrealms=Dynamic Realms
+realm.auditHistory.title=Realm ${fullPath} history
diff --git 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms_ru.properties
 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms_ru.properties
index d1d026042e..66a1ce9c90 100644
--- 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms_ru.properties
+++ 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Realms_ru.properties
@@ -23,3 +23,4 @@ realmLabel=Realm
 dynRealmLabel=Dynamic Realm
 realms=Realms
 dynrealms=Dynamic Realms
+realm.auditHistory.title=Realm ${fullPath} history
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAuditEventDAO.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAuditEventDAO.java
index 1a12103a42..180338ea1b 100644
--- 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAuditEventDAO.java
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAuditEventDAO.java
@@ -51,10 +51,10 @@ public class JPAAuditEventDAO implements AuditEventDAO {
         protected AuditEventCriteriaBuilder entityKey(final String entityKey) {
             if (entityKey != null) {
                 query.append(andIfNeeded()).
-                        append("(before_value LIKE 
'%key%").append(entityKey).append("%' OR ").
-                        append("inputs LIKE 
'%key%").append(entityKey).append("%' OR ").
-                        append("output LIKE 
'%key%").append(entityKey).append("%' OR ").
-                        append("throwable LIKE 
'%key%").append(entityKey).append("%')");
+                        append("(before_value LIKE 
'%\"key\":\"").append(entityKey).append("\"%' OR ").
+                        append("inputs LIKE 
'%\"key\":\"").append(entityKey).append("\"%' OR ").
+                        append("output LIKE 
'%\"key\":\"").append(entityKey).append("\"%' OR ").
+                        append("throwable LIKE 
'%\"key\":\"").append(entityKey).append("\"%')");
             }
             return this;
         }
diff --git 
a/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAuditEventDAO.java
 
b/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAuditEventDAO.java
index 481ef19833..c654ff1917 100644
--- 
a/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAuditEventDAO.java
+++ 
b/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAuditEventDAO.java
@@ -99,7 +99,7 @@ public class ElasticsearchAuditEventDAO implements 
AuditEventDAO {
                     multiMatch(QueryBuilders.multiMatch().
                             fields("before", "inputs", "output", "throwable").
                             type(TextQueryType.Phrase).
-                            query(entityKey).build()).build());
+                            query("\"key\":\"" + entityKey + 
"\"").build()).build());
         }
 
         queries.add(new Query.Builder().regexp(QueryBuilders.regexp().
diff --git 
a/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAuditEventDAO.java
 
b/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAuditEventDAO.java
index 71f487bfb5..abcf7035c1 100644
--- 
a/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAuditEventDAO.java
+++ 
b/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAuditEventDAO.java
@@ -98,7 +98,7 @@ public class OpenSearchAuditEventDAO implements AuditEventDAO 
{
                     multiMatch(QueryBuilders.multiMatch().
                             fields("before", "inputs", "output", "throwable").
                             type(TextQueryType.Phrase).
-                            query(entityKey).build()).build());
+                            query("\"key\":\"" + entityKey + 
"\"").build()).build());
         }
 
         queries.add(new Query.Builder().regexp(QueryBuilders.regexp().

Reply via email to