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


The following commit(s) were added to refs/heads/4_0_X by this push:
     new 4741990a8d [SYNCOPE-1892] Macro: commands pipe and binary form 
properties (#1134)
4741990a8d is described below

commit 4741990a8ddf3342a78b42dab987dee49d445389
Author: Francesco Chicchiriccò <ilgro...@users.noreply.github.com>
AuthorDate: Tue Jul 15 17:04:55 2025 +0200

    [SYNCOPE-1892] Macro: commands pipe and binary form properties (#1134)
---
 .../clientapps/ClientAppModalPanelBuilder.java     |   4 +-
 .../policies/AttrReleasePolicyModalPanel.java      |   2 +-
 .../console/wizards/AuthModuleWizardBuilder.java   |   2 +-
 .../markup/html/list/ConnConfPropertyListView.java |   2 +-
 .../wizards/any/LinkedAccountPlainAttrsPanel.java  |   2 +-
 .../client/ui/commons/BaseWebApplication.java      |  15 +-
 .../client/ui/commons/ImplementationLookup.java    |  12 +-
 .../syncope/client/ui}/commons/PreviewUtils.java   |  10 +-
 .../markup/html/form/BaseBinaryFieldPanel.java     |  36 ---
 .../markup/html/form/BinaryFieldPanel.java         |  26 +-
 .../commons}/markup/html/form/MultiFieldPanel.java |   8 +-
 .../client/ui/commons/panels/SyncopeFormPanel.java |  20 ++
 .../markup/html/form/BinaryFieldPanel.html         |   0
 .../commons}/markup/html/form/MultiFieldPanel.html |   0
 .../client/console/IdRepoConsoleContext.java       |   2 +-
 .../client/console/SyncopeWebApplication.java      |   8 +-
 .../init/ClassPathScanImplementationLookup.java    |   6 +-
 .../notifications/NotificationWizardBuilder.java   |   2 +-
 .../syncope/client/console/panels/BeanPanel.java   |   2 +-
 .../console/panels/DashboardSystemPanel.java       |   2 +-
 .../console/panels/ParametersWizardAttrStep.java   |   4 +-
 .../console/panels/search/AbstractSearchPanel.java |   2 +-
 .../console/tasks/FormPropertyDefsPanel.java       |  29 +++
 .../client/console/wizards/AttrWizardBuilder.java  |   2 +-
 .../wizards/any/AbstractAttrsWizardStep.java       |   4 +-
 .../console/wizards/any/ConnObjectPanel.java       |   2 +-
 .../console/wizards/role/RoleWizardBuilder.java    |   2 +-
 .../console/implementations/MyCommand.groovy       |   5 +-
 .../console/implementations/MyMacroActions.groovy  |   2 +-
 .../console/tasks/FormPropertyDefsPanel.html       |   1 +
 .../console/tasks/FormPropertyDefsPanel.properties |   1 +
 .../tasks/FormPropertyDefsPanel_fr_CA.properties   |   1 +
 .../tasks/FormPropertyDefsPanel_it.properties      |   1 +
 .../tasks/FormPropertyDefsPanel_ja.properties      |   1 +
 .../tasks/FormPropertyDefsPanel_pt_BR.properties   |   1 +
 .../tasks/FormPropertyDefsPanel_ru.properties      |   1 +
 .../client/enduser/IdRepoEnduserContext.java       |   2 +-
 .../client/enduser/SyncopeWebApplication.java      |  14 +-
 .../client/enduser/commons/PreviewUtils.java       |  59 -----
 .../init/ClassPathScanImplementationLookup.java    |   6 +-
 .../enduser/markup/html/form/BinaryFieldPanel.java | 270 ---------------------
 .../enduser/markup/html/form/MultiFieldPanel.java  | 121 ---------
 .../client/enduser/panels/any/PlainAttrs.java      |   4 +-
 .../enduser/markup/html/form/BinaryFieldPanel.html |  48 ----
 .../enduser/markup/html/form/MultiFieldPanel.html  |  72 ------
 .../syncope/common/lib/command/CommandArgs.java    |  29 +++
 .../syncope/common/lib/form/FormProperty.java      |  12 +
 .../syncope/common/lib/form/FormPropertyType.java  |   3 +-
 .../syncope/common/lib/to/FormPropertyDefTO.java   |  12 +
 .../apache/syncope/core/logic/CommandLogic.java    |   4 +-
 .../api/entity/task/FormPropertyDef.java           |   6 +-
 .../jpa/entity/task/JPAFormPropertyDef.java        |  16 +-
 .../neo4j/entity/task/Neo4jFormPropertyDef.java    |  16 +-
 .../core/provisioning/api/macro/Command.java       |   3 +-
 .../core/provisioning/api/macro/MacroActions.java  |   3 +-
 .../provisioning/java/data/TaskDataBinderImpl.java |   8 +-
 .../provisioning/java/job/MacroJobDelegate.java    |  19 +-
 .../dao/ElasticsearchAnySearchDAO.java             |   2 +
 .../console/wizards/OIDCProviderWizardBuilder.java |   2 +-
 .../opensearch/dao/OpenSearchAnySearchDAO.java     |   2 +
 .../client/console/panels/SCIMConfUserPanel.java   |   2 +-
 fit/core-reference/pom.xml                         |  28 +++
 .../syncope/fit/core/reference/TestCommand.java    |   8 +-
 .../src/test/resources/GroovyCommand.groovy        |   5 +-
 pom.xml                                            |   6 +-
 65 files changed, 307 insertions(+), 695 deletions(-)

diff --git 
a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java
 
b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java
index 784023fbe0..211ff1fce4 100644
--- 
a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java
+++ 
b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java
@@ -40,8 +40,6 @@ import 
org.apache.syncope.client.console.rest.PolicyRestClient;
 import org.apache.syncope.client.console.rest.RealmRestClient;
 import 
org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
 import 
org.apache.syncope.client.console.wicket.markup.html.form.AjaxSearchFieldPanel;
-import 
org.apache.syncope.client.console.wicket.markup.html.form.BinaryFieldPanel;
-import 
org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
 import 
org.apache.syncope.client.console.wicket.markup.html.form.PolicyRenderer;
 import org.apache.syncope.client.ui.commons.Constants;
 import 
org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
@@ -50,6 +48,8 @@ import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoiceP
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxNumberFieldPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.BinaryFieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
 import org.apache.syncope.client.ui.commons.panels.WizardModalPanel;
 import org.apache.syncope.client.ui.commons.wizards.AbstractModalPanelBuilder;
diff --git 
a/client/am/console/src/main/java/org/apache/syncope/client/console/policies/AttrReleasePolicyModalPanel.java
 
b/client/am/console/src/main/java/org/apache/syncope/client/console/policies/AttrReleasePolicyModalPanel.java
index 52bba25e29..ca413343d8 100644
--- 
a/client/am/console/src/main/java/org/apache/syncope/client/console/policies/AttrReleasePolicyModalPanel.java
+++ 
b/client/am/console/src/main/java/org/apache/syncope/client/console/policies/AttrReleasePolicyModalPanel.java
@@ -26,7 +26,6 @@ import 
org.apache.syncope.client.console.panels.AbstractModalPanel;
 import org.apache.syncope.client.console.rest.AttrRepoRestClient;
 import org.apache.syncope.client.console.rest.PolicyRestClient;
 import 
org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
-import 
org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.client.ui.commons.Constants;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
@@ -34,6 +33,7 @@ import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxGridFieldPanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxNumberFieldPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
 import org.apache.syncope.common.lib.policy.AttrReleasePolicyTO;
 import 
org.apache.syncope.common.lib.policy.DefaultAttrReleasePolicyConf.PrincipalAttrRepoMergingStrategy;
diff --git 
a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder.java
 
b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder.java
index 6afacdc038..92677033f0 100644
--- 
a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder.java
+++ 
b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AuthModuleWizardBuilder.java
@@ -27,7 +27,6 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.SyncopeWebApplication;
 import org.apache.syncope.client.console.panels.BeanPanel;
 import org.apache.syncope.client.console.rest.AuthModuleRestClient;
-import 
org.apache.syncope.client.console.wicket.markup.html.form.BinaryFieldPanel;
 import 
org.apache.syncope.client.console.wicket.markup.html.form.XMLEditorPanel;
 import 
org.apache.syncope.client.console.wizards.mapping.AuthModuleMappingPanel;
 import org.apache.syncope.client.ui.commons.Constants;
@@ -36,6 +35,7 @@ import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxNumberFieldPanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.BinaryFieldPanel;
 import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
 import org.apache.syncope.common.lib.AbstractLDAPConf;
 import org.apache.syncope.common.lib.auth.AuthModuleConf;
diff --git 
a/client/idm/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/list/ConnConfPropertyListView.java
 
b/client/idm/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/list/ConnConfPropertyListView.java
index f45b5d8472..a6381081ac 100644
--- 
a/client/idm/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/list/ConnConfPropertyListView.java
+++ 
b/client/idm/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/list/ConnConfPropertyListView.java
@@ -25,7 +25,6 @@ import java.util.List;
 import org.apache.commons.lang3.ClassUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.commons.IdMConstants;
-import 
org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.client.ui.commons.Constants;
 import 
org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AbstractFieldPanel;
@@ -34,6 +33,7 @@ import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxNumberFieldPane
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxPasswordFieldPanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.common.lib.types.ConnConfProperty;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.markup.ComponentTag;
diff --git 
a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountPlainAttrsPanel.java
 
b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountPlainAttrsPanel.java
index 1c5ef73c08..29facf423c 100644
--- 
a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountPlainAttrsPanel.java
+++ 
b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/LinkedAccountPlainAttrsPanel.java
@@ -28,11 +28,11 @@ import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
 import 
org.apache.syncope.client.console.commons.LinkedAccountPlainAttrProperty;
-import 
org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.client.ui.commons.Constants;
 import 
org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AbstractFieldPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.MultiFieldPanel;
 import 
org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion;
 import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
 import org.apache.syncope.client.ui.commons.wizards.any.EntityWrapper;
diff --git 
a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/Command.java
 
b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/BaseWebApplication.java
similarity index 76%
copy from 
core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/Command.java
copy to 
client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/BaseWebApplication.java
index aea95cd227..6a86eb46eb 100644
--- 
a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/Command.java
+++ 
b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/BaseWebApplication.java
@@ -16,12 +16,17 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.provisioning.api.macro;
+package org.apache.syncope.client.ui.commons;
 
-import org.apache.syncope.common.lib.command.CommandArgs;
+public interface BaseWebApplication {
 
-@FunctionalInterface
-public interface Command<A extends CommandArgs> {
+    String getAdminUser();
 
-    String run(A args);
+    String getAnonymousUser();
+
+    String getAnonymousKey();
+
+    long getMaxWaitTimeInSeconds();
+
+    int getMaxUploadFileSizeMB();
 }
diff --git 
a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/Command.java
 
b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/ImplementationLookup.java
similarity index 72%
copy from 
core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/Command.java
copy to 
client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/ImplementationLookup.java
index aea95cd227..340837a664 100644
--- 
a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/Command.java
+++ 
b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/ImplementationLookup.java
@@ -16,12 +16,14 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.core.provisioning.api.macro;
+package org.apache.syncope.client.ui.commons;
 
-import org.apache.syncope.common.lib.command.CommandArgs;
+import java.io.Serializable;
+import 
org.apache.syncope.client.ui.commons.markup.html.form.preview.BinaryPreviewer;
 
-@FunctionalInterface
-public interface Command<A extends CommandArgs> {
+public interface ImplementationLookup extends Serializable {
 
-    String run(A args);
+    void load();
+
+    Class<? extends BinaryPreviewer> getPreviewerClass(String mimeType);
 }
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/PreviewUtils.java
 
b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/PreviewUtils.java
similarity index 85%
rename from 
client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/PreviewUtils.java
rename to 
client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/PreviewUtils.java
index 80ac9f86a6..1c9d8f2caa 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/PreviewUtils.java
+++ 
b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/PreviewUtils.java
@@ -16,12 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.client.console.commons;
+package org.apache.syncope.client.ui.commons;
 
 import java.io.Serializable;
 import java.util.Optional;
 import org.apache.commons.lang3.StringUtils;
-import 
org.apache.syncope.client.console.init.ClassPathScanImplementationLookup;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.preview.BinaryPreviewer;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.preview.DefaultPreviewer;
 import org.slf4j.Logger;
@@ -34,9 +33,9 @@ public class PreviewUtils implements Serializable {
 
     protected static final Logger LOG = 
LoggerFactory.getLogger(PreviewUtils.class);
 
-    protected final ClassPathScanImplementationLookup lookup;
+    protected final ImplementationLookup lookup;
 
-    public PreviewUtils(final ClassPathScanImplementationLookup lookup) {
+    public PreviewUtils(final ImplementationLookup lookup) {
         this.lookup = lookup;
     }
 
@@ -47,8 +46,7 @@ public class PreviewUtils implements Serializable {
 
         return 
Optional.ofNullable(lookup.getPreviewerClass(mimeType)).map(clazz -> {
             try {
-                return ClassUtils.getConstructorIfAvailable(clazz, 
String.class).
-                        newInstance(new Object[] { mimeType });
+                return ClassUtils.getConstructorIfAvailable(clazz, 
String.class).newInstance(new Object[] { mimeType });
             } catch (Exception e) {
                 LOG.error("While getting BinaryPreviewer for {}", mimeType, e);
 
diff --git 
a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/BaseBinaryFieldPanel.java
 
b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/BaseBinaryFieldPanel.java
deleted file mode 100644
index 75d644fa4d..0000000000
--- 
a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/BaseBinaryFieldPanel.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.ui.commons.markup.html.form;
-
-import org.apache.wicket.model.IModel;
-
-public abstract class BaseBinaryFieldPanel extends FieldPanel<String> {
-
-    private static final long serialVersionUID = -198988924922541273L;
-
-    public BaseBinaryFieldPanel(final String id, final IModel<String> model) {
-        super(id, model);
-    }
-
-    public BaseBinaryFieldPanel(final String id, final String name, final 
IModel<String> model) {
-        super(id, name, model);
-    }
-
-    protected abstract void sendError(Exception exception);
-}
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java
 
b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/BinaryFieldPanel.java
similarity index 91%
rename from 
client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java
rename to 
client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/BinaryFieldPanel.java
index 69e38e9d7e..1cf2ed1540 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.java
+++ 
b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/BinaryFieldPanel.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.client.console.wicket.markup.html.form;
+package org.apache.syncope.client.ui.commons.markup.html.form;
 
 import 
de.agilecoders.wicket.extensions.markup.html.bootstrap.form.fileinput.BootstrapFileInputField;
 import 
de.agilecoders.wicket.extensions.markup.html.bootstrap.form.fileinput.FileInputConfig;
@@ -29,18 +29,16 @@ import java.util.Base64;
 import java.util.Locale;
 import java.util.Optional;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.client.console.SyncopeConsoleSession;
-import org.apache.syncope.client.console.SyncopeWebApplication;
-import org.apache.syncope.client.console.commons.PreviewUtils;
+import org.apache.syncope.client.ui.commons.BaseSession;
+import org.apache.syncope.client.ui.commons.BaseWebApplication;
 import org.apache.syncope.client.ui.commons.Constants;
 import org.apache.syncope.client.ui.commons.HttpResourceStream;
-import 
org.apache.syncope.client.ui.commons.markup.html.form.BaseBinaryFieldPanel;
-import 
org.apache.syncope.client.ui.commons.markup.html.form.BinaryFieldDownload;
-import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
+import org.apache.syncope.client.ui.commons.PreviewUtils;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.preview.BinaryPreviewer;
 import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
 import org.apache.syncope.client.ui.commons.rest.ResponseHolder;
 import org.apache.wicket.Component;
+import org.apache.wicket.Session;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.form.AjaxFormSubmitBehavior;
 import org.apache.wicket.ajax.markup.html.AjaxLink;
@@ -55,10 +53,11 @@ import org.apache.wicket.markup.html.panel.Fragment;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.ResourceModel;
 import org.apache.wicket.model.util.ListModel;
+import org.apache.wicket.protocol.http.WebApplication;
 import org.apache.wicket.spring.injection.annot.SpringBean;
 import org.apache.wicket.util.lang.Bytes;
 
-public class BinaryFieldPanel extends BaseBinaryFieldPanel {
+public class BinaryFieldPanel extends FieldPanel<String> {
 
     private static final long serialVersionUID = 6264462604183088931L;
 
@@ -103,7 +102,7 @@ public class BinaryFieldPanel extends BaseBinaryFieldPanel {
 
         previewer = previewUtils.getPreviewer(mimeType);
 
-        maxUploadSize = 
Bytes.megabytes(SyncopeWebApplication.get().getMaxUploadFileSizeMB());
+        maxUploadSize = 
Bytes.megabytes(BaseWebApplication.class.cast(WebApplication.get()).getMaxUploadFileSizeMB());
         uploadForm = new StatelessForm<>("uploadForm");
         uploadForm.setMultiPart(true);
         add(uploadForm);
@@ -142,7 +141,7 @@ public class BinaryFieldPanel extends BaseBinaryFieldPanel {
                 try {
                     fileDownload.initiate(target);
                 } catch (Exception e) {
-                    SyncopeConsoleSession.get().onException(e);
+                    BaseSession.class.cast(Session.get()).onException(e);
                 }
             }
         };
@@ -152,7 +151,7 @@ public class BinaryFieldPanel extends BaseBinaryFieldPanel {
         FileInputConfig config = new FileInputConfig().
                 showUpload(false).showRemove(false).showPreview(false).
                 browseClass("btn btn-success").browseIcon("<i class=\"fas 
fa-folder-open\"></i> &nbsp;");
-        String language = 
SyncopeConsoleSession.get().getLocale().getLanguage();
+        String language = Session.get().getLocale().getLanguage();
         if (!Locale.ENGLISH.getLanguage().equals(language)) {
             config.withLocale(language);
         }
@@ -167,7 +166,7 @@ public class BinaryFieldPanel extends BaseBinaryFieldPanel {
                 if (uploaded != null) {
                     if (maxUploadSize != null && uploaded.getSize() > 
maxUploadSize.bytes()) {
                         // SYNCOPE-1213 manage directly max upload file size 
(if set in properties file)
-                        
SyncopeConsoleSession.get().error(getString("tooLargeFile").
+                        Session.get().error(getString("tooLargeFile").
                                 replace("${maxUploadSizeB}", 
String.valueOf(maxUploadSize.bytes())).
                                 replace("${maxUploadSizeMB}", 
String.valueOf(maxUploadSize.bytes() / 1000000L)));
                         ((BaseWebPage) 
getPageReference().getPage()).getNotificationPanel().refresh(target);
@@ -258,9 +257,8 @@ public class BinaryFieldPanel extends BaseBinaryFieldPanel {
         return this;
     }
 
-    @Override
     protected void sendError(final Exception exception) {
-        SyncopeConsoleSession.get().onException(exception);
+        BaseSession.class.cast(Session.get()).onException(exception);
     }
 
     @Override
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel.java
 
b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/MultiFieldPanel.java
similarity index 91%
rename from 
client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel.java
rename to 
client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/MultiFieldPanel.java
index 995f6fac21..bfa04ae685 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel.java
+++ 
b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/MultiFieldPanel.java
@@ -16,15 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.client.console.wicket.markup.html.form;
+package org.apache.syncope.client.ui.commons.markup.html.form;
 
 import java.io.Serializable;
 import java.util.List;
-import org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.client.ui.commons.Constants;
 import 
org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
-import 
org.apache.syncope.client.ui.commons.markup.html.form.AbstractMultiPanel;
-import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
+import org.apache.wicket.Session;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.markup.html.list.ListItem;
 import org.apache.wicket.markup.html.panel.Panel;
@@ -113,7 +111,7 @@ public abstract class MultiFieldPanel<E extends 
Serializable> extends AbstractMu
 
                 @Override
                 protected void sendError(final String message) {
-                    
SyncopeConsoleSession.get().error(getString(Constants.OPERATION_ERROR));
+                    Session.get().error(getString(Constants.OPERATION_ERROR));
                 }
             };
         }
diff --git 
a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/panels/SyncopeFormPanel.java
 
b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/panels/SyncopeFormPanel.java
index b31b39699e..d827d28aa6 100644
--- 
a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/panels/SyncopeFormPanel.java
+++ 
b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/panels/SyncopeFormPanel.java
@@ -39,9 +39,11 @@ import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxNumberFieldPane
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxPasswordFieldPanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.BinaryFieldPanel;
 import org.apache.syncope.common.lib.form.FormProperty;
 import org.apache.syncope.common.lib.form.FormPropertyValue;
 import org.apache.syncope.common.lib.form.SyncopeForm;
+import org.apache.wicket.PageReference;
 import org.apache.wicket.markup.html.list.ListItem;
 import org.apache.wicket.markup.html.list.ListView;
 import org.apache.wicket.markup.html.panel.Panel;
@@ -208,6 +210,24 @@ public class SyncopeFormPanel<F extends SyncopeForm> 
extends Panel {
                                 setResetPassword(false);
                         break;
 
+                    case Binary:
+                        PageReference pageRef = getPage().getPageReference();
+                        field = new BinaryFieldPanel(
+                                "value",
+                                label,
+                                new PropertyModel<>(prop, "value"),
+                                prop.getMimeType(),
+                                prop.getId()) {
+
+                            private static final long serialVersionUID = 
-3268213909514986831L;
+
+                            @Override
+                            protected PageReference getPageReference() {
+                                return pageRef;
+                            }
+                        };
+                        break;
+
                     case String:
                     default:
                         field = new AjaxTextFieldPanel("value", label, new 
PropertyModel<>(prop, "value"), false);
diff --git 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.html
 
b/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/markup/html/form/BinaryFieldPanel.html
similarity index 100%
rename from 
client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/BinaryFieldPanel.html
rename to 
client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/markup/html/form/BinaryFieldPanel.html
diff --git 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel.html
 
b/client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/markup/html/form/MultiFieldPanel.html
similarity index 100%
rename from 
client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/MultiFieldPanel.html
rename to 
client/idrepo/common-ui/src/main/resources/org/apache/syncope/client/ui/commons/markup/html/form/MultiFieldPanel.html
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/IdRepoConsoleContext.java
 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/IdRepoConsoleContext.java
index a18e81a251..7750d736b9 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/IdRepoConsoleContext.java
+++ 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/IdRepoConsoleContext.java
@@ -35,7 +35,6 @@ import 
org.apache.syncope.client.console.commons.IdRepoRealmPolicyProvider;
 import org.apache.syncope.client.console.commons.IdRepoStatusProvider;
 import org.apache.syncope.client.console.commons.ImplementationInfoProvider;
 import org.apache.syncope.client.console.commons.PolicyTabProvider;
-import org.apache.syncope.client.console.commons.PreviewUtils;
 import org.apache.syncope.client.console.commons.RealmPolicyProvider;
 import org.apache.syncope.client.console.commons.StatusProvider;
 import 
org.apache.syncope.client.console.init.ClassPathScanImplementationContributor;
@@ -65,6 +64,7 @@ import org.apache.syncope.client.console.rest.TaskRestClient;
 import org.apache.syncope.client.console.rest.UserRestClient;
 import org.apache.syncope.client.console.rest.UserSelfRestClient;
 import org.apache.syncope.client.ui.commons.MIMETypesLoader;
+import org.apache.syncope.client.ui.commons.PreviewUtils;
 import 
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeWebApplication.java
 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeWebApplication.java
index 0f48e51b26..ade79641a5 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeWebApplication.java
+++ 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeWebApplication.java
@@ -45,6 +45,7 @@ import org.apache.syncope.client.console.rest.RealmRestClient;
 import org.apache.syncope.client.console.wizards.any.UserFormFinalizer;
 import org.apache.syncope.client.lib.SyncopeAnonymousClient;
 import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
+import org.apache.syncope.client.ui.commons.BaseWebApplication;
 import org.apache.syncope.client.ui.commons.Constants;
 import org.apache.syncope.client.ui.commons.SyncopeUIRequestCycleListener;
 import org.apache.syncope.client.ui.commons.annotations.Resource;
@@ -73,7 +74,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.aop.support.AopUtils;
 
-public class SyncopeWebApplication extends WicketBootSecuredWebApplication {
+public class SyncopeWebApplication extends WicketBootSecuredWebApplication 
implements BaseWebApplication {
 
     protected static final Logger LOG = 
LoggerFactory.getLogger(SyncopeWebApplication.class);
 
@@ -286,22 +287,27 @@ public class SyncopeWebApplication extends 
WicketBootSecuredWebApplication {
         return props.getDefaultAnyPanelClass();
     }
 
+    @Override
     public String getAdminUser() {
         return props.getAdminUser();
     }
 
+    @Override
     public String getAnonymousUser() {
         return props.getAnonymousUser();
     }
 
+    @Override
     public String getAnonymousKey() {
         return props.getAnonymousKey();
     }
 
+    @Override
     public long getMaxWaitTimeInSeconds() {
         return props.getMaxWaitTimeOnApplyChanges();
     }
 
+    @Override
     public int getMaxUploadFileSizeMB() {
         return props.getMaxUploadFileSizeMB();
     }
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/init/ClassPathScanImplementationLookup.java
 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/init/ClassPathScanImplementationLookup.java
index 67ffcd53ee..6b26769a2e 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/init/ClassPathScanImplementationLookup.java
+++ 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/init/ClassPathScanImplementationLookup.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.client.console.init;
 
-import java.io.Serializable;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
@@ -39,6 +38,7 @@ import org.apache.syncope.client.console.pages.BaseExtPage;
 import org.apache.syncope.client.console.pages.BasePage;
 import org.apache.syncope.client.console.widgets.BaseExtWidget;
 import org.apache.syncope.client.console.widgets.ExtAlertWidget;
+import org.apache.syncope.client.ui.commons.ImplementationLookup;
 import org.apache.syncope.client.ui.commons.annotations.AMPage;
 import org.apache.syncope.client.ui.commons.annotations.BinaryPreview;
 import org.apache.syncope.client.ui.commons.annotations.ExtPage;
@@ -59,7 +59,7 @@ import 
org.springframework.context.annotation.ClassPathScanningCandidateComponen
 import org.springframework.core.type.filter.AssignableTypeFilter;
 import org.springframework.util.ClassUtils;
 
-public class ClassPathScanImplementationLookup implements Serializable {
+public class ClassPathScanImplementationLookup implements ImplementationLookup 
{
 
     private static final long serialVersionUID = 5047756409117925203L;
 
@@ -151,6 +151,7 @@ public class ClassPathScanImplementationLookup implements 
Serializable {
     }
 
     @SuppressWarnings("unchecked")
+    @Override
     public void load() {
         classes = new HashMap<>();
         idRepoPages = new ArrayList<>();
@@ -271,6 +272,7 @@ public class ClassPathScanImplementationLookup implements 
Serializable {
                 collect(Collectors.toList());
     }
 
+    @Override
     public Class<? extends BinaryPreviewer> getPreviewerClass(final String 
mimeType) {
         LOG.debug("Searching for previewer class for MIME type: {}", mimeType);
 
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java
 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java
index 50bb34560d..b58288c48e 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java
+++ 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/notifications/NotificationWizardBuilder.java
@@ -37,13 +37,13 @@ import 
org.apache.syncope.client.console.rest.AnyTypeRestClient;
 import org.apache.syncope.client.console.rest.ImplementationRestClient;
 import org.apache.syncope.client.console.rest.NotificationRestClient;
 import org.apache.syncope.client.console.rest.SchemaRestClient;
-import 
org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.client.console.wicket.markup.html.form.MultiPanel;
 import org.apache.syncope.client.console.wizards.BaseAjaxWizardBuilder;
 import org.apache.syncope.client.ui.commons.Constants;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.AnyTypeTO;
 import org.apache.syncope.common.lib.to.DerSchemaTO;
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/BeanPanel.java
 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/BeanPanel.java
index 34473862d3..f030a86765 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/BeanPanel.java
+++ 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/BeanPanel.java
@@ -46,7 +46,6 @@ import 
org.apache.syncope.client.console.panels.search.SearchClause;
 import org.apache.syncope.client.console.panels.search.SearchUtils;
 import org.apache.syncope.client.console.panels.search.UserSearchPanel;
 import org.apache.syncope.client.console.rest.SchemaRestClient;
-import 
org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.client.ui.commons.DateOps;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
@@ -57,6 +56,7 @@ import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxNumberFieldPane
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.common.lib.report.SearchCondition;
 import org.apache.syncope.common.lib.search.AbstractFiqlSearchConditionBuilder;
 import org.apache.syncope.common.lib.to.SchemaTO;
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DashboardSystemPanel.java
 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DashboardSystemPanel.java
index c3c6446f99..59dd4a6243 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DashboardSystemPanel.java
+++ 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DashboardSystemPanel.java
@@ -24,10 +24,10 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.client.console.rest.SyncopeRestClient;
-import 
org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.client.ui.commons.HttpResourceStream;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxNumberFieldPanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.client.ui.commons.rest.ResponseHolder;
 import org.apache.syncope.common.lib.info.SystemInfo;
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/ParametersWizardAttrStep.java
 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/ParametersWizardAttrStep.java
index 895822ace9..8bf69dee54 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/ParametersWizardAttrStep.java
+++ 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/ParametersWizardAttrStep.java
@@ -21,13 +21,13 @@ package org.apache.syncope.client.console.panels;
 import java.util.List;
 import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.time.DateFormatUtils;
-import 
org.apache.syncope.client.console.wicket.markup.html.form.BinaryFieldPanel;
-import 
org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxDateTimeFieldPanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxNumberFieldPanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.BinaryFieldPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
 import org.apache.syncope.common.lib.to.PlainSchemaTO;
 import org.apache.wicket.extensions.wizard.WizardStep;
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/AbstractSearchPanel.java
 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/AbstractSearchPanel.java
index c399c170ef..bbe164e84e 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/AbstractSearchPanel.java
+++ 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/AbstractSearchPanel.java
@@ -32,7 +32,7 @@ import 
org.apache.syncope.client.console.rest.FIQLQueryRestClient;
 import org.apache.syncope.client.console.rest.GroupRestClient;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
-import 
org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.search.AbstractFiqlSearchConditionBuilder;
 import org.apache.syncope.common.lib.search.SearchableFields;
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.java
 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.java
index f27ed56a56..6a902b9445 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.java
+++ 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.java
@@ -33,6 +33,7 @@ import 
org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.Bas
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
 import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel;
 import org.apache.syncope.client.ui.commons.Constants;
+import org.apache.syncope.client.ui.commons.MIMETypesLoader;
 import 
org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoicePanel;
@@ -64,6 +65,9 @@ public class FormPropertyDefsPanel extends 
AbstractModalPanel<MacroTaskTO> {
     @SpringBean
     protected TaskRestClient taskRestClient;
 
+    @SpringBean
+    protected MIMETypesLoader mimeTypesLoader;
+
     protected final MacroTaskTO task;
 
     protected final IModel<List<FormPropertyDefTO>> model;
@@ -182,6 +186,15 @@ public class FormPropertyDefsPanel extends 
AbstractModalPanel<MacroTaskTO> {
                 enumValues.setVisible(fpd.getType() == FormPropertyType.Enum);
                 
item.add(enumValues.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true));
 
+                AjaxTextFieldPanel mimeType = new AjaxTextFieldPanel(
+                        "mimeType",
+                        "mimeType",
+                        new PropertyModel<>(fpd, "mimeType"),
+                        true);
+                mimeType.setChoices(mimeTypesLoader.getMimeTypes());
+                mimeType.setVisible(fpd.getType() == FormPropertyType.Binary);
+                
item.add(mimeType.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true));
+
                 WebMarkupContainer dropdownConf = new 
WebMarkupContainer("dropdownConf");
                 dropdownConf.setVisible(fpd.getType() == 
FormPropertyType.Dropdown);
                 
item.add(dropdownConf.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true));
@@ -211,6 +224,7 @@ public class FormPropertyDefsPanel extends 
AbstractModalPanel<MacroTaskTO> {
                                 enumValues.setVisible(false);
                                 fpd.getEnumValues().clear();
                                 dropdownConf.setVisible(false);
+                                mimeType.setVisible(false);
                             }
 
                             case Date -> {
@@ -220,6 +234,7 @@ public class FormPropertyDefsPanel extends 
AbstractModalPanel<MacroTaskTO> {
                                 enumValues.setVisible(false);
                                 fpd.getEnumValues().clear();
                                 dropdownConf.setVisible(false);
+                                mimeType.setVisible(false);
                             }
 
                             case Enum -> {
@@ -228,6 +243,7 @@ public class FormPropertyDefsPanel extends 
AbstractModalPanel<MacroTaskTO> {
                                 datePattern.setVisible(false);
                                 enumValues.setVisible(true);
                                 dropdownConf.setVisible(false);
+                                mimeType.setVisible(false);
                             }
 
                             case Dropdown -> {
@@ -237,6 +253,17 @@ public class FormPropertyDefsPanel extends 
AbstractModalPanel<MacroTaskTO> {
                                 enumValues.setVisible(false);
                                 fpd.getEnumValues().clear();
                                 dropdownConf.setVisible(true);
+                                mimeType.setVisible(false);
+                            }
+
+                            case Binary -> {
+                                stringRegEx.setVisible(false);
+                                fpd.setStringRegEx(null);
+                                datePattern.setVisible(false);
+                                enumValues.setVisible(false);
+                                fpd.getEnumValues().clear();
+                                dropdownConf.setVisible(false);
+                                mimeType.setVisible(true);
                             }
 
                             default -> {
@@ -246,6 +273,7 @@ public class FormPropertyDefsPanel extends 
AbstractModalPanel<MacroTaskTO> {
                                 enumValues.setVisible(false);
                                 fpd.getEnumValues().clear();
                                 dropdownConf.setVisible(false);
+                                mimeType.setVisible(false);
                             }
                         }
 
@@ -253,6 +281,7 @@ public class FormPropertyDefsPanel extends 
AbstractModalPanel<MacroTaskTO> {
                         target.add(datePattern);
                         target.add(enumValues);
                         target.add(dropdownConf);
+                        target.add(mimeType);
                     }
                 });
 
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/AttrWizardBuilder.java
 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/AttrWizardBuilder.java
index 16009194b5..58146784ba 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/AttrWizardBuilder.java
+++ 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/AttrWizardBuilder.java
@@ -18,9 +18,9 @@
  */
 package org.apache.syncope.client.console.wizards;
 
-import 
org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.client.ui.commons.Constants;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.common.lib.Attr;
 import org.apache.wicket.PageReference;
 import org.apache.wicket.extensions.wizard.WizardModel;
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrsWizardStep.java
 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrsWizardStep.java
index 5d44b37412..847340f0bf 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrsWizardStep.java
+++ 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AbstractAttrsWizardStep.java
@@ -35,8 +35,6 @@ import org.apache.commons.lang3.time.FastDateFormat;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.client.console.rest.AnyTypeClassRestClient;
 import org.apache.syncope.client.console.rest.SchemaRestClient;
-import 
org.apache.syncope.client.console.wicket.markup.html.form.BinaryFieldPanel;
-import 
org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.client.ui.commons.ajax.markup.html.LabelInfo;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AbstractFieldPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
@@ -46,8 +44,10 @@ import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoiceP
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxNumberFieldPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.BinaryFieldPanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.EncryptedFieldPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
 import org.apache.syncope.common.lib.Attr;
 import org.apache.syncope.common.lib.SyncopeConstants;
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/ConnObjectPanel.java
 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/ConnObjectPanel.java
index f69bb8a543..c1be95dc03 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/ConnObjectPanel.java
+++ 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/ConnObjectPanel.java
@@ -27,9 +27,9 @@ import java.util.stream.Collectors;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.ListUtils;
 import org.apache.commons.lang3.tuple.Pair;
-import 
org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.client.ui.commons.ConnIdSpecialName;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.common.lib.Attr;
 import org.apache.syncope.common.lib.EntityTOUtils;
 import org.apache.syncope.common.lib.to.ConnObject;
diff --git 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleWizardBuilder.java
 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleWizardBuilder.java
index 28abc47ef3..ab836e14be 100644
--- 
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleWizardBuilder.java
+++ 
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleWizardBuilder.java
@@ -31,13 +31,13 @@ import 
org.apache.syncope.client.console.rest.DynRealmRestClient;
 import org.apache.syncope.client.console.rest.RealmRestClient;
 import org.apache.syncope.client.console.rest.RoleRestClient;
 import 
org.apache.syncope.client.console.wicket.markup.html.form.AjaxSearchFieldPanel;
-import 
org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.client.console.wizards.BaseAjaxWizardBuilder;
 import org.apache.syncope.client.ui.commons.Constants;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AbstractFieldPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.MultiFieldPanel;
 import 
org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion;
 import org.apache.syncope.client.ui.commons.wizards.AjaxWizardBuilder;
 import org.apache.syncope.common.lib.to.DynRealmTO;
diff --git 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyCommand.groovy
 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyCommand.groovy
index 3dd7c7fcf9..e9625f72da 100644
--- 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyCommand.groovy
+++ 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyCommand.groovy
@@ -23,7 +23,8 @@ import org.apache.syncope.core.provisioning.api.macro.Command
 @CompileStatic
 class MyCommand implements Command<CommandArgs> {
 
-  String run(CommandArgs args) {
-    return "SUCCESS"
+  @Override
+  CommandArgs.Result run(CommandArgs args) {
+    return new CommandArgs.Result("SUCCESS")    
   }
 }
diff --git 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyMacroActions.groovy
 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyMacroActions.groovy
index 1edd24358a..fa9e2d6c77 100644
--- 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyMacroActions.groovy
+++ 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyMacroActions.groovy
@@ -51,7 +51,7 @@ class MyMacroActions implements MacroActions {
   }
 
   @Override
-  void afterCommand(Command<CommandArgs> command, CommandArgs args, String 
output) {
+  void afterCommand(Command<CommandArgs> command, CommandArgs args, 
CommandArgs.Result result) {
   }
 
   @Override
diff --git 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.html
 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.html
index 7ab57b2801..950d153961 100644
--- 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.html
+++ 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.html
@@ -62,6 +62,7 @@ under the License.
                 <span wicket:id="dropdownSingleSelection"/>
                 <span wicket:id="dropdownFreeForm"/>
               </div>
+              <span wicket:id="mimeType"/>
             </td>
             <td>
               <div id="inline-actions">
diff --git 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.properties
 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.properties
index c61b80b25d..150056bfd4 100644
--- 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.properties
+++ 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel.properties
@@ -25,3 +25,4 @@ datePattern=Pattern
 dropdownSingleSelection=Single Selection
 dropdownFreeForm=Free Form
 labels=Labels
+mimeType=MIME Type
diff --git 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_fr_CA.properties
 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_fr_CA.properties
index c61b80b25d..150056bfd4 100644
--- 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_fr_CA.properties
+++ 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_fr_CA.properties
@@ -25,3 +25,4 @@ datePattern=Pattern
 dropdownSingleSelection=Single Selection
 dropdownFreeForm=Free Form
 labels=Labels
+mimeType=MIME Type
diff --git 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_it.properties
 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_it.properties
index aa7ba2735a..a824800a86 100644
--- 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_it.properties
+++ 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_it.properties
@@ -25,3 +25,4 @@ datePattern=Modello
 dropdownSingleSelection=Selezione Singola
 dropdownFreeForm=Modalit\u00e0 libera
 labels=Etichette
+mimeType=MIME Type
diff --git 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_ja.properties
 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_ja.properties
index c61b80b25d..150056bfd4 100644
--- 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_ja.properties
+++ 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_ja.properties
@@ -25,3 +25,4 @@ datePattern=Pattern
 dropdownSingleSelection=Single Selection
 dropdownFreeForm=Free Form
 labels=Labels
+mimeType=MIME Type
diff --git 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_pt_BR.properties
 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_pt_BR.properties
index c61b80b25d..150056bfd4 100644
--- 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_pt_BR.properties
+++ 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_pt_BR.properties
@@ -25,3 +25,4 @@ datePattern=Pattern
 dropdownSingleSelection=Single Selection
 dropdownFreeForm=Free Form
 labels=Labels
+mimeType=MIME Type
diff --git 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_ru.properties
 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_ru.properties
index c61b80b25d..150056bfd4 100644
--- 
a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_ru.properties
+++ 
b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/FormPropertyDefsPanel_ru.properties
@@ -25,3 +25,4 @@ datePattern=Pattern
 dropdownSingleSelection=Single Selection
 dropdownFreeForm=Free Form
 labels=Labels
+mimeType=MIME Type
diff --git 
a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/IdRepoEnduserContext.java
 
b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/IdRepoEnduserContext.java
index 2d4a2e5a4e..4cbe7b47be 100644
--- 
a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/IdRepoEnduserContext.java
+++ 
b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/IdRepoEnduserContext.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.client.enduser;
 
-import org.apache.syncope.client.enduser.commons.PreviewUtils;
 import 
org.apache.syncope.client.enduser.init.ClassPathScanImplementationLookup;
 import org.apache.syncope.client.enduser.rest.AnyTypeRestClient;
 import org.apache.syncope.client.enduser.rest.GroupRestClient;
@@ -27,6 +26,7 @@ import 
org.apache.syncope.client.enduser.rest.SecurityQuestionRestClient;
 import org.apache.syncope.client.enduser.rest.SyncopeRestClient;
 import org.apache.syncope.client.enduser.rest.UserSelfRestClient;
 import org.apache.syncope.client.ui.commons.MIMETypesLoader;
+import org.apache.syncope.client.ui.commons.PreviewUtils;
 import 
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
diff --git 
a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeWebApplication.java
 
b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeWebApplication.java
index 52a5b894de..7324c59758 100644
--- 
a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeWebApplication.java
+++ 
b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeWebApplication.java
@@ -39,6 +39,7 @@ import 
org.apache.syncope.client.enduser.pages.SelfConfirmPasswordReset;
 import org.apache.syncope.client.enduser.panels.Sidebar;
 import org.apache.syncope.client.lib.SyncopeAnonymousClient;
 import org.apache.syncope.client.lib.SyncopeClientFactoryBean;
+import org.apache.syncope.client.ui.commons.BaseWebApplication;
 import org.apache.syncope.client.ui.commons.SyncopeUIRequestCycleListener;
 import org.apache.syncope.client.ui.commons.annotations.Resource;
 import org.apache.syncope.client.ui.commons.themes.AdminLTE;
@@ -69,7 +70,7 @@ import org.slf4j.LoggerFactory;
 import org.springframework.aop.support.AopUtils;
 import org.springframework.core.io.ResourceLoader;
 
-public class SyncopeWebApplication extends WicketBootSecuredWebApplication {
+public class SyncopeWebApplication extends WicketBootSecuredWebApplication 
implements BaseWebApplication {
 
     protected static final Logger LOG = 
LoggerFactory.getLogger(SyncopeWebApplication.class);
 
@@ -290,14 +291,21 @@ public class SyncopeWebApplication extends 
WicketBootSecuredWebApplication {
         return Login.class;
     }
 
+    @Override
     public String getAdminUser() {
         return props.getAdminUser();
     }
 
+    @Override
     public String getAnonymousUser() {
         return props.getAnonymousUser();
     }
 
+    @Override
+    public String getAnonymousKey() {
+        return props.getAnonymousKey();
+    }
+
     public boolean isCaptchaEnabled() {
         return props.isCaptcha();
     }
@@ -314,11 +322,13 @@ public class SyncopeWebApplication extends 
WicketBootSecuredWebApplication {
         return props.isReportPropagationErrorDetails();
     }
 
+    @Override
     public long getMaxWaitTimeInSeconds() {
         return props.getMaxWaitTimeOnApplyChanges();
     }
 
-    public Integer getMaxUploadFileSizeMB() {
+    @Override
+    public int getMaxUploadFileSizeMB() {
         return props.getMaxUploadFileSizeMB();
     }
 }
diff --git 
a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/commons/PreviewUtils.java
 
b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/commons/PreviewUtils.java
deleted file mode 100644
index d32418d608..0000000000
--- 
a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/commons/PreviewUtils.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.enduser.commons;
-
-import java.io.Serializable;
-import java.util.Optional;
-import org.apache.commons.lang3.StringUtils;
-import 
org.apache.syncope.client.enduser.init.ClassPathScanImplementationLookup;
-import 
org.apache.syncope.client.ui.commons.markup.html.form.preview.BinaryPreviewer;
-import 
org.apache.syncope.client.ui.commons.markup.html.form.preview.DefaultPreviewer;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.util.ClassUtils;
-
-public class PreviewUtils implements Serializable {
-
-    private static final long serialVersionUID = 2765845550328631887L;
-
-    protected static final Logger LOG = 
LoggerFactory.getLogger(PreviewUtils.class);
-
-    protected final ClassPathScanImplementationLookup lookup;
-
-    public PreviewUtils(final ClassPathScanImplementationLookup lookup) {
-        this.lookup = lookup;
-    }
-
-    public BinaryPreviewer getPreviewer(final String mimeType) {
-        if (StringUtils.isBlank(mimeType)) {
-            return new DefaultPreviewer(mimeType);
-        }
-
-        return 
Optional.ofNullable(lookup.getPreviewerClass(mimeType)).map(clazz -> {
-            try {
-                return ClassUtils.getConstructorIfAvailable(clazz, 
String.class).
-                        newInstance(new Object[] { mimeType });
-            } catch (Exception e) {
-                LOG.error("While getting BinaryPreviewer for {}", mimeType, e);
-
-                return new DefaultPreviewer(mimeType);
-            }
-        }).orElseGet(() -> new DefaultPreviewer(mimeType));
-    }
-}
diff --git 
a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/init/ClassPathScanImplementationLookup.java
 
b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/init/ClassPathScanImplementationLookup.java
index d5c4d2b34b..69e81dc201 100644
--- 
a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/init/ClassPathScanImplementationLookup.java
+++ 
b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/init/ClassPathScanImplementationLookup.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.client.enduser.init;
 
-import java.io.Serializable;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -28,6 +27,7 @@ import java.util.Objects;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.syncope.client.enduser.pages.BaseExtPage;
 import org.apache.syncope.client.enduser.pages.BasePage;
+import org.apache.syncope.client.ui.commons.ImplementationLookup;
 import org.apache.syncope.client.ui.commons.annotations.AMPage;
 import org.apache.syncope.client.ui.commons.annotations.BinaryPreview;
 import org.apache.syncope.client.ui.commons.annotations.ExtPage;
@@ -41,7 +41,7 @@ import 
org.springframework.context.annotation.ClassPathScanningCandidateComponen
 import org.springframework.core.type.filter.AssignableTypeFilter;
 import org.springframework.util.ClassUtils;
 
-public class ClassPathScanImplementationLookup implements Serializable {
+public class ClassPathScanImplementationLookup implements ImplementationLookup 
{
 
     private static final long serialVersionUID = -4944986595429290116L;
 
@@ -69,6 +69,7 @@ public class ClassPathScanImplementationLookup implements 
Serializable {
     }
 
     @SuppressWarnings("unchecked")
+    @Override
     public void load() {
         idmPages = new ArrayList<>();
         amPages = new ArrayList<>();
@@ -143,6 +144,7 @@ public class ClassPathScanImplementationLookup implements 
Serializable {
         return extPages;
     }
 
+    @Override
     public Class<? extends BinaryPreviewer> getPreviewerClass(final String 
mimeType) {
         LOG.debug("Searching for previewer class for MIME type: {}", mimeType);
         Class<? extends BinaryPreviewer> previewer = null;
diff --git 
a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/markup/html/form/BinaryFieldPanel.java
 
b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/markup/html/form/BinaryFieldPanel.java
deleted file mode 100644
index 3110681d9d..0000000000
--- 
a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/markup/html/form/BinaryFieldPanel.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.enduser.markup.html.form;
-
-import 
de.agilecoders.wicket.extensions.markup.html.bootstrap.form.fileinput.BootstrapFileInputField;
-import 
de.agilecoders.wicket.extensions.markup.html.bootstrap.form.fileinput.FileInputConfig;
-import jakarta.ws.rs.core.HttpHeaders;
-import jakarta.ws.rs.core.MediaType;
-import jakarta.ws.rs.core.Response;
-import java.io.ByteArrayInputStream;
-import java.util.ArrayList;
-import java.util.Base64;
-import java.util.Locale;
-import java.util.Optional;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.client.enduser.SyncopeEnduserSession;
-import org.apache.syncope.client.enduser.SyncopeWebApplication;
-import org.apache.syncope.client.enduser.commons.PreviewUtils;
-import org.apache.syncope.client.ui.commons.Constants;
-import org.apache.syncope.client.ui.commons.HttpResourceStream;
-import 
org.apache.syncope.client.ui.commons.markup.html.form.BaseBinaryFieldPanel;
-import 
org.apache.syncope.client.ui.commons.markup.html.form.BinaryFieldDownload;
-import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
-import 
org.apache.syncope.client.ui.commons.markup.html.form.preview.BinaryPreviewer;
-import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
-import org.apache.syncope.client.ui.commons.rest.ResponseHolder;
-import org.apache.wicket.Component;
-import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.ajax.form.AjaxFormSubmitBehavior;
-import org.apache.wicket.ajax.markup.html.AjaxLink;
-import org.apache.wicket.extensions.ajax.markup.html.IndicatingAjaxLink;
-import org.apache.wicket.markup.html.WebMarkupContainer;
-import org.apache.wicket.markup.html.basic.Label;
-import org.apache.wicket.markup.html.form.Form;
-import org.apache.wicket.markup.html.form.StatelessForm;
-import org.apache.wicket.markup.html.form.TextField;
-import org.apache.wicket.markup.html.form.upload.FileUpload;
-import org.apache.wicket.markup.html.panel.Fragment;
-import org.apache.wicket.model.IModel;
-import org.apache.wicket.model.ResourceModel;
-import org.apache.wicket.model.util.ListModel;
-import org.apache.wicket.spring.injection.annot.SpringBean;
-import org.apache.wicket.util.lang.Bytes;
-
-public class BinaryFieldPanel extends BaseBinaryFieldPanel {
-
-    private static final long serialVersionUID = 6264462604183088931L;
-
-    @SpringBean
-    protected PreviewUtils previewUtils;
-
-    protected final String mimeType;
-
-    protected final WebMarkupContainer container;
-
-    protected final AjaxLink<Void> downloadLink;
-
-    protected final Form<?> uploadForm;
-
-    protected final Fragment emptyFragment;
-
-    protected final BootstrapFileInputField fileUpload;
-
-    protected final BinaryFieldDownload fileDownload;
-
-    protected final BinaryPreviewer previewer;
-
-    protected final IndicatingAjaxLink<Void> resetLink;
-
-    protected final Bytes maxUploadSize;
-
-    protected final IModel<String> model;
-
-    protected final String fileKey;
-
-    public BinaryFieldPanel(
-            final String id,
-            final String name,
-            final IModel<String> model,
-            final String mimeType,
-            final String fileKey) {
-
-        super(id, name, model);
-        this.model = model;
-        this.fileKey = fileKey;
-        this.mimeType = mimeType;
-
-        previewer = previewUtils.getPreviewer(mimeType);
-
-        maxUploadSize = SyncopeWebApplication.get().getMaxUploadFileSizeMB() 
== null
-                ? null
-                : 
Bytes.megabytes(SyncopeWebApplication.get().getMaxUploadFileSizeMB());
-        uploadForm = new StatelessForm<>("uploadForm");
-        uploadForm.setMultiPart(true);
-        add(uploadForm);
-
-        container = new WebMarkupContainer("previewContainer");
-        container.setOutputMarkupId(true);
-
-        emptyFragment = new Fragment("panelPreview", "emptyFragment", 
container);
-        emptyFragment.setOutputMarkupId(true);
-        container.add(emptyFragment);
-        uploadForm.add(container);
-
-        field = new TextField<>("textField", model);
-        add(field.setLabel(new ResourceModel(name, 
name)).setOutputMarkupId(true));
-
-        uploadForm.add(new Label("preview", StringUtils.isBlank(mimeType) ? 
StringUtils.EMPTY : '(' + mimeType + ')'));
-
-        fileDownload = new BinaryFieldDownload(name, fileKey, mimeType, true) {
-
-            private static final long serialVersionUID = 7203445884857810583L;
-
-            @Override
-            protected HttpResourceStream getResourceStream() {
-                return new HttpResourceStream(new 
ResponseHolder(buildResponse()));
-            }
-        };
-
-        add(fileDownload);
-
-        downloadLink = new AjaxLink<>("downloadLink") {
-
-            private static final long serialVersionUID = -4331619903296515985L;
-
-            @Override
-            public void onClick(final AjaxRequestTarget target) {
-                try {
-                    fileDownload.initiate(target);
-                } catch (Exception e) {
-                    SyncopeEnduserSession.get().onException(e);
-                }
-            }
-        };
-        downloadLink.setOutputMarkupId(true);
-        uploadForm.add(downloadLink);
-
-        FileInputConfig config = new FileInputConfig().
-                showUpload(false).showRemove(false).showPreview(false).
-                browseClass("btn btn-success").browseIcon("<i class=\"fas 
fa-folder-open\"></i> &nbsp;");
-        String language = 
SyncopeEnduserSession.get().getLocale().getLanguage();
-        if (!Locale.ENGLISH.getLanguage().equals(language)) {
-            config.withLocale(language);
-        }
-        fileUpload = new BootstrapFileInputField("fileUpload", new 
ListModel<>(new ArrayList<>()), config);
-        fileUpload.add(new AjaxFormSubmitBehavior(Constants.ON_CHANGE) {
-
-            private static final long serialVersionUID = -1107858522700306810L;
-
-            @Override
-            protected void onSubmit(final AjaxRequestTarget target) {
-                FileUpload uploaded = fileUpload.getFileUpload();
-                if (uploaded != null) {
-                    if (maxUploadSize != null && uploaded.getSize() > 
maxUploadSize.bytes()) {
-                        // SYNCOPE-1213 manage directly max upload file size 
(if set in properties file)
-                        
SyncopeEnduserSession.get().error(getString("tooLargeFile").
-                                replace("${maxUploadSizeB}", 
String.valueOf(maxUploadSize.bytes())).
-                                replace("${maxUploadSizeMB}", 
String.valueOf(maxUploadSize.bytes() / 1000000L)));
-                        ((BaseWebPage) 
getPageReference().getPage()).getNotificationPanel().refresh(target);
-                    } else {
-                        byte[] uploadedBytes = uploaded.getBytes();
-                        String uploadedEncoded = 
Base64.getEncoder().encodeToString(uploadedBytes);
-                        field.setModelObject(uploadedEncoded);
-                        target.add(field);
-
-                        Component panelPreview = 
previewer.preview(uploadedBytes);
-                        changePreviewer(panelPreview);
-                        fileUpload.setModelObject(null);
-                        uploadForm.addOrReplace(fileUpload);
-
-                        
setVisibleFileButtons(StringUtils.isNotBlank(uploadedEncoded));
-                        
downloadLink.setEnabled(StringUtils.isNotBlank(uploadedEncoded));
-
-                        target.add(uploadForm);
-                    }
-                }
-            }
-        });
-        uploadForm.add(fileUpload);
-
-        resetLink = new IndicatingAjaxLink<>("resetLink") {
-
-            private static final long serialVersionUID = -7978723352517770644L;
-
-            @Override
-            public void onClick(final AjaxRequestTarget target) {
-                field.setModelObject(null);
-                target.add(field);
-                downloadLink.setEnabled(false);
-                container.addOrReplace(emptyFragment);
-                setVisibleFileButtons(false);
-                target.add(uploadForm);
-            }
-
-            @Override
-            public String getAjaxIndicatorMarkupId() {
-                return Constants.VEIL_INDICATOR_MARKUP_ID;
-            }
-
-        };
-        uploadForm.add(resetLink);
-    }
-
-    protected Response buildResponse() {
-        return Response.ok(new 
ByteArrayInputStream(Base64.getMimeDecoder().decode(getModelObject()))).
-                type(StringUtils.isBlank(mimeType) ? 
MediaType.APPLICATION_OCTET_STREAM : mimeType).
-                header(HttpHeaders.LOCATION, StringUtils.EMPTY).
-                build();
-    }
-
-    protected void changePreviewer(final Component panelPreview) {
-        Fragment fragment = new Fragment("panelPreview", "previewFragment", 
container);
-        fragment.add(panelPreview);
-        container.addOrReplace(fragment);
-        uploadForm.addOrReplace(container);
-    }
-
-    protected void setVisibleFileButtons(final boolean visible) {
-        resetLink.setVisible(visible);
-        downloadLink.setVisible(visible);
-    }
-
-    @Override
-    public BinaryFieldPanel clone() {
-        LOG.debug("Custom clone for binary field panel...");
-        return new BinaryFieldPanel(getId(), this.name, this.model, 
this.mimeType, this.fileKey);
-    }
-
-    @Override
-    public FieldPanel<String> setNewModel(final IModel<String> model) {
-        field.setModel(model);
-        String modelObj = model.getObject();
-
-        if (StringUtils.isNotBlank(modelObj)) {
-            
Optional.ofNullable(previewer.preview(modelObj)).ifPresent(this::changePreviewer);
-        }
-
-        downloadLink.setEnabled(StringUtils.isNotBlank(modelObj));
-        setVisibleFileButtons(StringUtils.isNotBlank(modelObj));
-        return this;
-    }
-
-    @Override
-    protected void sendError(final Exception exception) {
-        SyncopeEnduserSession.get().onException(exception);
-    }
-
-    @Override
-    public FieldPanel<String> setReadOnly(final boolean readOnly) {
-        super.setReadOnly(readOnly);
-        fileUpload.setEnabled(!readOnly);
-        return this;
-    }
-}
diff --git 
a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/markup/html/form/MultiFieldPanel.java
 
b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/markup/html/form/MultiFieldPanel.java
deleted file mode 100644
index 4d813983cc..0000000000
--- 
a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/markup/html/form/MultiFieldPanel.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.client.enduser.markup.html.form;
-
-import java.io.Serializable;
-import java.util.List;
-import org.apache.syncope.client.enduser.SyncopeEnduserSession;
-import org.apache.syncope.client.ui.commons.Constants;
-import 
org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
-import 
org.apache.syncope.client.ui.commons.markup.html.form.AbstractMultiPanel;
-import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
-import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.markup.html.list.ListItem;
-import org.apache.wicket.markup.html.panel.Panel;
-import org.apache.wicket.model.IModel;
-
-public abstract class MultiFieldPanel<E extends Serializable> extends 
AbstractMultiPanel<E> {
-
-    private static final long serialVersionUID = -6322397761456513324L;
-
-    private MultiFieldPanel(
-            final String id,
-            final String name,
-            final IModel<List<E>> model) {
-
-        super(id, name, model);
-    }
-
-    public static class Builder<E extends Serializable> implements 
Serializable {
-
-        private static final long serialVersionUID = -5304396077613727937L;
-
-        private final IModel<List<E>> model;
-
-        private boolean eventTemplate = false;
-
-        public Builder(final IModel<List<E>> model) {
-            this.model = model;
-        }
-
-        /**
-         * Set on_change event in order to send MultiValueSelectorEvent to 
page.
-         *
-         * @param eventTemplate whether this is an event template
-         * @return this instance, for fluent building
-         */
-        public Builder<E> setEventTemplate(final boolean eventTemplate) {
-            this.eventTemplate = eventTemplate;
-            return this;
-        }
-
-        /**
-         * Default model object instance.
-         *
-         * @return default model object instance.
-         */
-        protected E newModelObject() {
-            return null;
-        }
-
-        public MultiFieldPanel<E> build(final String id, final String name, 
final FieldPanel<E> panelTemplate) {
-            return new MultiFieldPanel<>(id, name, model) {
-
-                private static final long serialVersionUID = 
6600411297376841521L;
-
-                @Override
-                protected E newModelObject() {
-                    return Builder.this.newModelObject();
-                }
-
-                @Override
-                protected FieldPanel<? extends Serializable> 
getItemPanel(final ListItem<E> item) {
-                    final FieldPanel<? extends Serializable> fieldPanel = 
panelTemplate.clone();
-                    fieldPanel.setIndex(item.getIndex());
-                    fieldPanel.setNewModel(item);
-                    fieldPanel.settingsDependingComponents();
-                    fieldPanel.hideLabel();
-
-                    if (eventTemplate) {
-                        fieldPanel.getField().add(new 
IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
-
-                            private static final long serialVersionUID = 
-1107858522700306810L;
-
-                            @Override
-                            protected void onUpdate(final AjaxRequestTarget 
target) {
-                            }
-                        });
-                    }
-
-                    return fieldPanel;
-                }
-
-                @Override
-                protected void clearInput(final Panel panel) {
-                    FieldPanel.class.cast(panel).getField().clearInput();
-                }
-
-                @Override
-                protected void sendError(final String message) {
-                    
SyncopeEnduserSession.get().error(getString(Constants.OPERATION_ERROR));
-                }
-            };
-        }
-    }
-}
diff --git 
a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/PlainAttrs.java
 
b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/PlainAttrs.java
index e84effb9f7..7f97d2b2ec 100644
--- 
a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/PlainAttrs.java
+++ 
b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/panels/any/PlainAttrs.java
@@ -28,8 +28,6 @@ import org.apache.commons.lang3.time.DateFormatUtils;
 import org.apache.commons.lang3.time.FastDateFormat;
 import org.apache.syncope.client.enduser.SyncopeEnduserSession;
 import org.apache.syncope.client.enduser.layout.CustomizationOption;
-import org.apache.syncope.client.enduser.markup.html.form.BinaryFieldPanel;
-import org.apache.syncope.client.enduser.markup.html.form.MultiFieldPanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AbstractFieldPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxDateFieldPanel;
@@ -38,8 +36,10 @@ import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoiceP
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxNumberFieldPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.BinaryFieldPanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.EncryptedFieldPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.MultiFieldPanel;
 import 
org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion;
 import org.apache.syncope.client.ui.commons.wizards.any.UserWrapper;
 import org.apache.syncope.common.lib.Attr;
diff --git 
a/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/markup/html/form/BinaryFieldPanel.html
 
b/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/markup/html/form/BinaryFieldPanel.html
deleted file mode 100644
index f17c0e47ba..0000000000
--- 
a/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/markup/html/form/BinaryFieldPanel.html
+++ /dev/null
@@ -1,48 +0,0 @@
-<!DOCTYPE html>
-<!--
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License.  You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing,
-software distributed under the License is distributed on an
-"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, either express or implied.  See the License for the
-specific language governing permissions and limitations
-under the License.
--->
-<html xmlns="http://www.w3.org/1999/xhtml"; 
xmlns:wicket="http://wicket.apache.org";>
-  <wicket:extend>
-    <wicket:enclosure child="field-label">
-      <label class="form-label" wicket:id="field-label">[LABEL]</label><span 
wicket:id="required"/>
-      <span wicket:id="externalAction"/>
-    </wicket:enclosure>
-    <fieldset class="input-group">
-      <input style="display: none;" wicket:id="textField"/>
-      <div>
-        <form wicket:id="uploadForm" encType="multipart/form-data" 
method="post" accept-charset="UTF-8">
-          <span wicket:id="previewContainer">
-            <span wicket:id="panelPreview">[panelPreview]</span>
-            <wicket:fragment wicket:id="previewFragment">
-              <div wicket:id = "previewer"></div>
-            </wicket:fragment>
-            <wicket:fragment wicket:id="emptyFragment">
-            </wicket:fragment>
-          </span>
-          <input wicket:id="fileUpload" type="file"/>
-          <div>
-            <a href="#" wicket:id="downloadLink"><i title="download" alt="file 
download icon" class="fa fa-arrow-alt-circle-down"></i></a>
-            <a href="#" wicket:id="resetLink"><i title="remove value" 
alt="remove value icon" class="fa fa-times"></i></a>
-            <span wicket:id="preview"/>
-          </div>
-        </form>
-      </div>
-    </fieldset>
-  </wicket:extend>
-</html>
diff --git 
a/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/markup/html/form/MultiFieldPanel.html
 
b/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/markup/html/form/MultiFieldPanel.html
deleted file mode 100644
index 1274ee7f61..0000000000
--- 
a/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/markup/html/form/MultiFieldPanel.html
+++ /dev/null
@@ -1,72 +0,0 @@
-<!DOCTYPE html>
-<!--
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License.  You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing,
-software distributed under the License is distributed on an
-"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, either express or implied.  See the License for the
-specific language governing permissions and limitations
-under the License.
--->
-<html xmlns="http://www.w3.org/1999/xhtml"; 
xmlns:wicket="http://wicket.apache.org";>
-  <head>
-    <title>Multivalue conatiner</title>
-  </head>
-  <body>
-    <wicket:extend>
-      <wicket:enclosure child="field-label">
-        <label class="form-label" wicket:id="field-label">[LABEL]</label><span 
wicket:id="required"/>
-        <span wicket:id="externalAction"/>
-      </wicket:enclosure>
-
-      <span wicket:id="multiValueContainer">
-        <form wicket:id="innerForm">
-          <span wicket:id="content">[content]</span>
-        </form>
-      </span>
-
-      <wicket:fragment wicket:id="noDataFragment">
-        <div class="input-group">
-          <div class="form-control" style="background-color:#EEE">
-            <label class="form-label" wicket:id="field-label">[LABEL]</label>
-          </div>
-          <span wicket:id="panelPlus">[plus]</span>
-        </div>
-      </wicket:fragment>
-
-      <wicket:fragment wicket:id="dataFragment">
-        <span wicket:id="view">
-          <div class="input-group">
-            <span wicket:id="panel">[form field]</span>
-            <span wicket:id="panelMinus">[minus]</span>
-            <span wicket:id="panelPlus">[plus]</span>
-          </div>
-        </span>
-      </wicket:fragment>
-
-      <wicket:fragment wicket:id="fragmentMinus">
-        <div class="input-group-addon me-1 ms-2">
-          <a wicket:id="drop"><i class="fa fa-minus"></i></a>
-        </div>
-      </wicket:fragment>
-
-      <wicket:fragment wicket:id="fragmentPlus">
-        <div class="input-group-addon">
-          <a wicket:id="add"><i class="fa fa-plus"></i></a>
-        </div>
-      </wicket:fragment>
-
-      <wicket:fragment wicket:id="emptyFragment">
-      </wicket:fragment>
-    </wicket:extend>
-  </body>
-</html>
diff --git 
a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/command/CommandArgs.java
 
b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/command/CommandArgs.java
index 5a66bae16d..cdc95fdfad 100644
--- 
a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/command/CommandArgs.java
+++ 
b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/command/CommandArgs.java
@@ -18,7 +18,13 @@
  */
 package org.apache.syncope.common.lib.command;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import java.io.Serializable;
+import java.util.Map;
+import java.util.Optional;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.cxf.common.util.CollectionUtils;
 import org.apache.syncope.common.lib.BaseBean;
 
 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, 
property = "_class")
@@ -26,4 +32,27 @@ public class CommandArgs implements BaseBean {
 
     private static final long serialVersionUID = -85050010490462751L;
 
+    public record Result(String message, Map<String, Serializable> values) 
implements Serializable {
+
+        public static final Result EMPTY = new Result(StringUtils.EMPTY, 
Map.of());
+
+        public Result(final String message) {
+            this(message, Map.of());
+        }
+
+        public boolean isEmpty() {
+            return StringUtils.isBlank(message) && 
CollectionUtils.isEmpty(values);
+        }
+    }
+
+    @JsonIgnore
+    private Result pipe;
+
+    public final Optional<Result> getPipe() {
+        return Optional.ofNullable(pipe);
+    }
+
+    public final void setPipe(final Result pipe) {
+        this.pipe = pipe;
+    }
 }
diff --git 
a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/FormProperty.java
 
b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/FormProperty.java
index dbf8745ef9..e0136cac8d 100644
--- 
a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/FormProperty.java
+++ 
b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/FormProperty.java
@@ -55,6 +55,8 @@ public class FormProperty implements Serializable {
 
     private boolean dropdownFreeForm;
 
+    private String mimeType;
+
     private String value;
 
     public String getId() {
@@ -149,6 +151,14 @@ public class FormProperty implements Serializable {
         this.dropdownFreeForm = dropdownFreeForm;
     }
 
+    public String getMimeType() {
+        return mimeType;
+    }
+
+    public void setMimeType(final String mimeType) {
+        this.mimeType = mimeType;
+    }
+
     public String getValue() {
         return value;
     }
@@ -172,6 +182,7 @@ public class FormProperty implements Serializable {
                 append(dropdownValues).
                 append(dropdownSingleSelection).
                 append(dropdownFreeForm).
+                append(mimeType).
                 append(value).
                 build();
     }
@@ -201,6 +212,7 @@ public class FormProperty implements Serializable {
                 append(dropdownValues, other.dropdownValues).
                 append(dropdownSingleSelection, other.dropdownSingleSelection).
                 append(dropdownFreeForm, other.dropdownFreeForm).
+                append(mimeType, other.mimeType).
                 append(value, other.value).
                 build();
     }
diff --git 
a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/FormPropertyType.java
 
b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/FormPropertyType.java
index a2ca54fdfe..4fbddff230 100644
--- 
a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/FormPropertyType.java
+++ 
b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/form/FormPropertyType.java
@@ -26,6 +26,7 @@ public enum FormPropertyType {
     Date,
     Boolean,
     Dropdown,
-    Password
+    Password,
+    Binary
 
 }
diff --git 
a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/FormPropertyDefTO.java
 
b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/FormPropertyDefTO.java
index a7c5df45aa..d04c4f4b85 100644
--- 
a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/FormPropertyDefTO.java
+++ 
b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/FormPropertyDefTO.java
@@ -56,6 +56,8 @@ public class FormPropertyDefTO implements NamedEntityTO {
 
     private boolean dropdownFreeForm;
 
+    private String mimeType;
+
     @Override
     public String getKey() {
         return key;
@@ -153,6 +155,14 @@ public class FormPropertyDefTO implements NamedEntityTO {
         this.dropdownFreeForm = dropdownFreeForm;
     }
 
+    public String getMimeType() {
+        return mimeType;
+    }
+
+    public void setMimeType(final String mimeType) {
+        this.mimeType = mimeType;
+    }
+
     @Override
     public int hashCode() {
         return new HashCodeBuilder().
@@ -168,6 +178,7 @@ public class FormPropertyDefTO implements NamedEntityTO {
                 append(enumValues).
                 append(dropdownSingleSelection).
                 append(dropdownFreeForm).
+                append(mimeType).
                 build();
     }
 
@@ -196,6 +207,7 @@ public class FormPropertyDefTO implements NamedEntityTO {
                 append(enumValues, other.enumValues).
                 append(dropdownSingleSelection, other.dropdownSingleSelection).
                 append(dropdownFreeForm, other.dropdownFreeForm).
+                append(mimeType, other.mimeType).
                 build();
     }
 }
diff --git 
a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/CommandLogic.java
 
b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/CommandLogic.java
index c4c36c99b5..4bd5b952c6 100644
--- 
a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/CommandLogic.java
+++ 
b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/CommandLogic.java
@@ -134,7 +134,9 @@ public class CommandLogic extends AbstractLogic<EntityTO> {
         }
 
         try {
-            return runnable.run(command.getArgs() == null ? 
ImplementationManager.emptyArgs(impl) : command.getArgs());
+            return runnable.run(command.getArgs() == null
+                    ? ImplementationManager.emptyArgs(impl)
+                    : command.getArgs()).message();
         } catch (Exception e) {
             LOG.error("While running {} on {}", command.getKey(), 
command.getArgs(), e);
 
diff --git 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/FormPropertyDef.java
 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/FormPropertyDef.java
index 551990a886..e8b556b29b 100644
--- 
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/FormPropertyDef.java
+++ 
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/FormPropertyDef.java
@@ -57,7 +57,7 @@ public interface FormPropertyDef extends Entity {
 
     Pattern getStringRegEx();
 
-    void setStringRegExp(Pattern stringRegEx);
+    void setStringRegEx(Pattern stringRegEx);
 
     String getDatePattern();
 
@@ -74,4 +74,8 @@ public interface FormPropertyDef extends Entity {
     boolean isDropdownFreeForm();
 
     void setDropdownFreeForm(boolean dropdownFreeForm);
+
+    String getMimeType();
+
+    void setMimeType(String mimeType);
 }
diff --git 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAFormPropertyDef.java
 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAFormPropertyDef.java
index 9083eea122..29600aeba1 100644
--- 
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAFormPropertyDef.java
+++ 
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAFormPropertyDef.java
@@ -101,6 +101,8 @@ public class JPAFormPropertyDef extends 
AbstractGeneratedKeyEntity implements Fo
     @NotNull
     private Boolean dropdownFreeForm = Boolean.FALSE;
 
+    private String mimeType;
+
     public void setIdx(final int idx) {
         this.idx = idx;
     }
@@ -182,7 +184,7 @@ public class JPAFormPropertyDef extends 
AbstractGeneratedKeyEntity implements Fo
     }
 
     @Override
-    public void setStringRegExp(final Pattern stringRegEx) {
+    public void setStringRegEx(final Pattern stringRegEx) {
         this.stringRegEx = 
Optional.ofNullable(stringRegEx).map(Pattern::pattern).orElse(null);
     }
 
@@ -199,7 +201,7 @@ public class JPAFormPropertyDef extends 
AbstractGeneratedKeyEntity implements Fo
     @Override
     public Map<String, String> getEnumValues() {
         return Optional.ofNullable(enumValues).map(v -> 
POJOHelper.deserialize(v, ENUMVALUES_TYPEREF)).
-            orElseGet(Map::of);
+                orElseGet(Map::of);
     }
 
     @Override
@@ -227,6 +229,16 @@ public class JPAFormPropertyDef extends 
AbstractGeneratedKeyEntity implements Fo
         this.dropdownFreeForm = dropdownFreeForm;
     }
 
+    @Override
+    public String getMimeType() {
+        return mimeType;
+    }
+
+    @Override
+    public void setMimeType(final String mimeType) {
+        this.mimeType = mimeType;
+    }
+
     protected void json2map(final boolean clearFirst) {
         if (clearFirst) {
             getLabels().clear();
diff --git 
a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jFormPropertyDef.java
 
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jFormPropertyDef.java
index 6859422375..079f1e4d11 100644
--- 
a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jFormPropertyDef.java
+++ 
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jFormPropertyDef.java
@@ -88,6 +88,8 @@ public class Neo4jFormPropertyDef extends 
AbstractGeneratedKeyNode implements Fo
     @NotNull
     private Boolean dropdownFreeForm = Boolean.FALSE;
 
+    private String mimeType;
+
     @Override
     public Neo4jMacroTask getMacroTask() {
         return macroTask;
@@ -165,7 +167,7 @@ public class Neo4jFormPropertyDef extends 
AbstractGeneratedKeyNode implements Fo
     }
 
     @Override
-    public void setStringRegExp(final Pattern stringRegEx) {
+    public void setStringRegEx(final Pattern stringRegEx) {
         this.stringRegEx = 
Optional.ofNullable(stringRegEx).map(Pattern::pattern).orElse(null);
     }
 
@@ -182,7 +184,7 @@ public class Neo4jFormPropertyDef extends 
AbstractGeneratedKeyNode implements Fo
     @Override
     public Map<String, String> getEnumValues() {
         return Optional.ofNullable(enumValues).map(v -> 
POJOHelper.deserialize(v, ENUMVALUES_TYPEREF)).
-            orElseGet(Map::of);
+                orElseGet(Map::of);
     }
 
     @Override
@@ -210,6 +212,16 @@ public class Neo4jFormPropertyDef extends 
AbstractGeneratedKeyNode implements Fo
         this.dropdownFreeForm = dropdownFreeForm;
     }
 
+    @Override
+    public String getMimeType() {
+        return mimeType;
+    }
+
+    @Override
+    public void setMimeType(final String mimeType) {
+        this.mimeType = mimeType;
+    }
+
     protected void json2map(final boolean clearFirst) {
         if (clearFirst) {
             getLabels().clear();
diff --git 
a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/Command.java
 
b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/Command.java
index aea95cd227..3a35474381 100644
--- 
a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/Command.java
+++ 
b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/Command.java
@@ -19,9 +19,10 @@
 package org.apache.syncope.core.provisioning.api.macro;
 
 import org.apache.syncope.common.lib.command.CommandArgs;
+import org.apache.syncope.common.lib.command.CommandArgs.Result;
 
 @FunctionalInterface
 public interface Command<A extends CommandArgs> {
 
-    String run(A args);
+    Result run(A args);
 }
diff --git 
a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/MacroActions.java
 
b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/MacroActions.java
index b0de301083..f48a3bc6d5 100644
--- 
a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/MacroActions.java
+++ 
b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/macro/MacroActions.java
@@ -22,6 +22,7 @@ import jakarta.validation.ValidationException;
 import java.util.Map;
 import java.util.Optional;
 import org.apache.syncope.common.lib.command.CommandArgs;
+import org.apache.syncope.common.lib.command.CommandArgs.Result;
 import org.apache.syncope.common.lib.form.SyncopeForm;
 
 /**
@@ -49,7 +50,7 @@ public interface MacroActions {
         // does nothing by default
     }
 
-    default void afterCommand(Command<CommandArgs> command, CommandArgs args, 
String output) {
+    default void afterCommand(Command<CommandArgs> command, CommandArgs args, 
Result result) {
         // does nothing by default
     }
 
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java
index 5864b17575..441f999d12 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java
@@ -333,12 +333,13 @@ public class TaskDataBinderImpl extends 
AbstractExecutableDatabinder implements
             fpd.setType(fpdTO.getType());
             fpd.setReadable(fpdTO.isReadable());
             fpd.setWritable(fpdTO.isWritable());
-            fpd.setStringRegExp(fpdTO.getStringRegEx());
+            fpd.setStringRegEx(fpdTO.getStringRegEx());
             fpd.setRequired(fpdTO.isRequired());
             fpd.setDatePattern(fpdTO.getDatePattern());
             fpd.setEnumValues(fpdTO.getEnumValues());
             fpd.setDropdownSingleSelection(fpdTO.isDropdownSingleSelection());
             fpd.setDropdownFreeForm(fpdTO.isDropdownFreeForm());
+            fpd.setMimeType(fpdTO.getMimeType());
 
             fpd.setMacroTask(macroTask);
             macroTask.add(fpd);
@@ -566,6 +567,7 @@ public class TaskDataBinderImpl extends 
AbstractExecutableDatabinder implements
                     fpdTO.getEnumValues().putAll(fpd.getEnumValues());
                     
fpdTO.setDropdownSingleSelection(fpd.isDropdownSingleSelection());
                     fpdTO.setDropdownFreeForm(fpd.isDropdownFreeForm());
+                    fpdTO.setMimeType(fpd.getMimeType());
 
                     macroTaskTO.getFormPropertyDefs().add(fpdTO);
                 });
@@ -711,6 +713,10 @@ public class TaskDataBinderImpl extends 
AbstractExecutableDatabinder implements
                     prop.setDropdownFreeForm(fpd.isDropdownFreeForm());
                 }
 
+                case Binary -> {
+                    prop.setMimeType(fpd.getMimeType());
+                }
+
                 default -> {
                 }
             }
diff --git 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/MacroJobDelegate.java
 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/MacroJobDelegate.java
index e2ed048ce1..ffcb5d3768 100644
--- 
a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/MacroJobDelegate.java
+++ 
b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/MacroJobDelegate.java
@@ -24,6 +24,7 @@ import jakarta.validation.ValidationException;
 import jakarta.validation.Validator;
 import java.time.format.DateTimeParseException;
 import java.util.ArrayList;
+import java.util.Base64;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -165,6 +166,9 @@ public class MacroJobDelegate extends 
AbstractSchedTaskJobDelegate<MacroTask> {
                     vars.put(fpd.getName(), value);
                 }
 
+                case Binary ->
+                    vars.put(fpd.getName(), Base64.getDecoder().decode(value));
+
                 default -> {
                 }
             }
@@ -194,6 +198,8 @@ public class MacroJobDelegate extends 
AbstractSchedTaskJobDelegate<MacroTask> {
 
                     Mutable<Pair<String, Throwable>> error = new 
MutableObject<>();
 
+                    Mutable<CommandArgs.Result> prevCmdResult = new 
MutableObject<>(CommandArgs.Result.EMPTY);
+
                     for (int i = 0; i < commands.size() && error.get() == 
null; i++) {
                         Pair<Command<CommandArgs>, CommandArgs> command = 
commands.get(i);
 
@@ -203,13 +209,18 @@ public class MacroJobDelegate extends 
AbstractSchedTaskJobDelegate<MacroTask> {
                                     append(args).append("\n");
 
                             if (!dryRun) {
-                                actions.ifPresent(a -> 
a.beforeCommand(command.getLeft(), command.getRight()));
+                                
command.getRight().setPipe(prevCmdResult.get());
+
+                                actions.ifPresent(a -> a.beforeCommand(
+                                        command.getLeft(), 
command.getRight()));
 
-                                String cmdOut = 
command.getLeft().run(command.getRight());
+                                CommandArgs.Result cmdResult = 
command.getLeft().run(command.getRight());
+                                prevCmdResult.setValue(cmdResult);
 
-                                actions.ifPresent(a -> 
a.afterCommand(command.getLeft(), command.getRight(), cmdOut));
+                                actions.ifPresent(a -> a.afterCommand(
+                                        command.getLeft(), command.getRight(), 
cmdResult));
 
-                                output.append(cmdOut);
+                                output.append(cmdResult.message());
                             }
                         } catch (Throwable t) {
                             if (task.isContinueOnError()) {
diff --git 
a/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAnySearchDAO.java
 
b/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAnySearchDAO.java
index c1cb6a9e42..697e7a59db 100644
--- 
a/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAnySearchDAO.java
+++ 
b/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAnySearchDAO.java
@@ -32,6 +32,7 @@ import 
co.elastic.clients.elasticsearch._types.query_dsl.RangeQuery;
 import co.elastic.clients.elasticsearch.core.CountRequest;
 import co.elastic.clients.elasticsearch.core.SearchRequest;
 import co.elastic.clients.elasticsearch.core.search.Hit;
+import co.elastic.clients.elasticsearch.core.search.SourceConfig;
 import co.elastic.clients.json.JsonData;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
@@ -280,6 +281,7 @@ public class ElasticsearchAnySearchDAO extends 
AbstractAnySearchDAO {
                 from(pageable.isUnpaged() ? 0 : pageable.getPageSize() * 
pageable.getPageNumber()).
                 size(pageable.isUnpaged() ? indexMaxResultWindow : 
pageable.getPageSize()).
                 sort(sortBuilders(kind, pageable.getSort().get())).
+                fields(List.of()).source(new 
SourceConfig.Builder().fetch(false).build()).
                 build();
         LOG.debug("Search JSON request: {}", request);
 
diff --git 
a/ext/oidcc4ui/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder.java
 
b/ext/oidcc4ui/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder.java
index 5487680e3a..6d53e47eb1 100644
--- 
a/ext/oidcc4ui/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder.java
+++ 
b/ext/oidcc4ui/client-console/src/main/java/org/apache/syncope/client/console/wizards/OIDCProviderWizardBuilder.java
@@ -30,7 +30,6 @@ import 
org.apache.syncope.client.console.SyncopeWebApplication;
 import org.apache.syncope.client.console.panels.OIDCProvidersDirectoryPanel;
 import org.apache.syncope.client.console.rest.ImplementationRestClient;
 import org.apache.syncope.client.console.rest.OIDCProviderRestClient;
-import 
org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
 import 
org.apache.syncope.client.console.wizards.mapping.ItemTransformersTogglePanel;
 import 
org.apache.syncope.client.console.wizards.mapping.JEXLTransformersTogglePanel;
 import 
org.apache.syncope.client.console.wizards.mapping.OIDCProviderMappingPanel;
@@ -39,6 +38,7 @@ import 
org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponent
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.MultiFieldPanel;
 import org.apache.syncope.client.ui.commons.wizards.AjaxWizardBuilder;
 import org.apache.syncope.common.lib.OIDCScopeConstants;
 import org.apache.syncope.common.lib.to.ImplementationTO;
diff --git 
a/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAnySearchDAO.java
 
b/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAnySearchDAO.java
index 5dfbdf9448..53712910bb 100644
--- 
a/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAnySearchDAO.java
+++ 
b/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAnySearchDAO.java
@@ -77,6 +77,7 @@ import 
org.opensearch.client.opensearch._types.query_dsl.QueryBuilders;
 import org.opensearch.client.opensearch.core.CountRequest;
 import org.opensearch.client.opensearch.core.SearchRequest;
 import org.opensearch.client.opensearch.core.search.Hit;
+import org.opensearch.client.opensearch.core.search.SourceConfig;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.domain.Sort;
 import org.springframework.util.CollectionUtils;
@@ -278,6 +279,7 @@ public class OpenSearchAnySearchDAO extends 
AbstractAnySearchDAO {
                 from(pageable.isUnpaged() ? 0 : pageable.getPageSize() * 
pageable.getPageNumber()).
                 size(pageable.isUnpaged() ? indexMaxResultWindow : 
pageable.getPageSize()).
                 sort(sortBuilders(kind, pageable.getSort().get())).
+                fields(List.of()).source(new 
SourceConfig.Builder().fetch(false).build()).
                 build();
         LOG.debug("Search JSON request: {}", request);
 
diff --git 
a/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfUserPanel.java
 
b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfUserPanel.java
index 1d8d408b39..97adfee5ca 100644
--- 
a/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfUserPanel.java
+++ 
b/ext/scimv2/client-console/src/main/java/org/apache/syncope/client/console/panels/SCIMConfUserPanel.java
@@ -20,8 +20,8 @@ package org.apache.syncope.client.console.panels;
 
 import java.util.ArrayList;
 import java.util.List;
-import 
org.apache.syncope.client.console.wicket.markup.html.form.MultiFieldPanel;
 import 
org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
+import org.apache.syncope.client.ui.commons.markup.html.form.MultiFieldPanel;
 import 
org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion;
 import org.apache.syncope.common.lib.scim.SCIMComplexConf;
 import org.apache.syncope.common.lib.scim.SCIMConf;
diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml
index 3a4c3948a9..7fc0404d36 100644
--- a/fit/core-reference/pom.xml
+++ b/fit/core-reference/pom.xml
@@ -559,6 +559,7 @@ under the License.
             <configuration>
               <images>
                 <image>
+                  <alias>elasticsearch</alias>
                   
<name>docker.elastic.co/elasticsearch/elasticsearch:${elasticsearch.version}</name>
                   <run>
                     <env>
@@ -575,6 +576,19 @@ under the License.
                     </ports>
                   </run>
                 </image>
+                <image>
+                  <alias>kibana</alias>
+                  
<name>docker.elastic.co/kibana/kibana:${elasticsearch.version}</name>
+                  <run>
+                    <env>
+                      
<ELASTICSEARCH_HOSTS>http://${docker.container.elasticsearch.ip}:9200</ELASTICSEARCH_HOSTS>
+                      <xpack.security.enabled>false</xpack.security.enabled>
+                    </env>
+                    <ports>
+                      <port>5601:5601</port>
+                    </ports>
+                  </run>
+                </image>
               </images>
             </configuration>
             <executions>
@@ -669,6 +683,7 @@ under the License.
             <configuration>
               <images>
                 <image>
+                  <alias>opensearch</alias>
                   
<name>opensearchproject/opensearch:${opensearch.version}</name>
                   <run>
                     <env>
@@ -685,6 +700,19 @@ under the License.
                     </ports>
                   </run>
                 </image>
+                <image>
+                  <alias>dashboards</alias>
+                  
<name>opensearchproject/opensearch-dashboards:${opensearch.version}</name>
+                  <run>
+                    <env>
+                      
<OPENSEARCH_HOSTS>http://${docker.container.opensearch.ip}:9200</OPENSEARCH_HOSTS>
+                      
<DISABLE_SECURITY_DASHBOARDS_PLUGIN>true</DISABLE_SECURITY_DASHBOARDS_PLUGIN>
+                    </env>
+                    <ports>
+                      <port>5601:5601</port>
+                    </ports>
+                  </run>
+                </image>
               </images>
             </configuration>
             <executions>
diff --git 
a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestCommand.java
 
b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestCommand.java
index ae5d83859e..1e8f6b4003 100644
--- 
a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestCommand.java
+++ 
b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestCommand.java
@@ -18,9 +18,11 @@
  */
 package org.apache.syncope.fit.core.reference;
 
+import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import org.apache.syncope.common.lib.Attr;
+import org.apache.syncope.common.lib.command.CommandArgs.Result;
 import org.apache.syncope.common.lib.request.AnyObjectCR;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.common.lib.to.RealmTO;
@@ -51,7 +53,7 @@ public class TestCommand implements Command<TestCommandArgs> {
 
     @Transactional(propagation = Propagation.NOT_SUPPORTED)
     @Override
-    public String run(final TestCommandArgs args) {
+    public Result run(final TestCommandArgs args) {
         // 1. create new Realm
         RealmTO realm = new RealmTO();
         realm.setName(args.getRealmName());
@@ -67,6 +69,8 @@ public class TestCommand implements Command<TestCommandArgs> {
                 false).getEntity();
         LOG.info("PRINTER created: {}", anyObject.getName());
 
-        return "Realm created: " + realm.getFullPath() + "; PRINTER created: " 
+ anyObject.getName();
+        return new Result(
+                "Realm created: " + realm.getFullPath() + "; PRINTER created: 
" + anyObject.getName(),
+                Map.of("realm", realm.getKey(), "PRINTER", 
anyObject.getKey()));
     }
 }
diff --git a/fit/core-reference/src/test/resources/GroovyCommand.groovy 
b/fit/core-reference/src/test/resources/GroovyCommand.groovy
index 7dcfd5b025..c092833b68 100644
--- a/fit/core-reference/src/test/resources/GroovyCommand.groovy
+++ b/fit/core-reference/src/test/resources/GroovyCommand.groovy
@@ -27,7 +27,8 @@ class GroovyCommand implements Command<CommandArgs> {
   @Autowired
   SyncopeLogic logic;
 
-  String run(CommandArgs args) {
-    return "" + logic.isPwdResetAllowed()
+  @Override
+  CommandArgs.Result run(CommandArgs args) {
+    return new CommandArgs.Result("" + logic.isPwdResetAllowed())    
   }
 }
diff --git a/pom.xml b/pom.xml
index b09765ee2c..656e25952b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -416,7 +416,7 @@ under the License.
     <nimbus-jose-jwt.version>10.3.1</nimbus-jose-jwt.version>
 
     <spring-boot.version>3.4.7</spring-boot.version>
-    <spring-cloud-gateway.version>4.2.3</spring-cloud-gateway.version>
+    <spring-cloud-gateway.version>4.2.4</spring-cloud-gateway.version>
 
     <openjpa.version>4.1.1</openjpa.version>
 
@@ -447,7 +447,7 @@ under the License.
     <cas-client.version>4.0.4</cas-client.version>
 
     <swagger-core.version>2.2.34</swagger-core.version>
-    <swagger-ui.version>5.25.3</swagger-ui.version>
+    <swagger-ui.version>5.26.2</swagger-ui.version>
 
     <jquery-slimscroll.version>1.3.8</jquery-slimscroll.version>
     <jquery-cookie.version>1.4.1-1</jquery-cookie.version>
@@ -1425,7 +1425,7 @@ under the License.
       <dependency>
         <groupId>org.springframework.cloud</groupId>
         <artifactId>spring-cloud-contract-wiremock</artifactId>
-        <version>4.2.1</version>
+        <version>4.2.2</version>
         <scope>test</scope>
       </dependency>
     </dependencies>

Reply via email to