You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2022/07/08 10:15:04 UTC

[syncope] branch master updated: [SYNCOPE-1688] Manage and reuse saved FIQL queries from Console (#359)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new b97e4af9f1 [SYNCOPE-1688] Manage and reuse saved FIQL queries from Console (#359)
b97e4af9f1 is described below

commit b97e4af9f1eb728051d719ea2bd52c28a897293d
Author: Francesco Chicchiriccò <il...@users.noreply.github.com>
AuthorDate: Fri Jul 8 12:14:59 2022 +0200

    [SYNCOPE-1688] Manage and reuse saved FIQL queries from Console (#359)
---
 .../authprofiles/AuthProfileWizardBuilder.java     |   2 +-
 .../console/wizards/AttrRepoWizardBuilder.java     |   4 +-
 .../console/wizards/AuthModuleWizardBuilder.java   |   9 +-
 .../console/panels/ConnObjectListViewPanel.java    | 106 +++++++------
 .../panels/search/ConnObjectSearchPanel.java       |  28 +++-
 .../console/topology/TopologyTogglePanel.java      |   3 +-
 .../any/MergeLinkedAccountsSearchPanel.java        |  13 +-
 .../client/ui/commons/CommonUIProperties.java      |  10 ++
 .../markup/html/form/AbstractFieldPanel.java       |  16 +-
 .../markup/html/form/AbstractMultiPanel.java       |   3 +-
 .../syncope/client/console/ConsoleProperties.java  |  12 ++
 .../client/console/SyncopeWebApplication.java      |   4 +
 .../client/console/commons/IdRepoConstants.java    |   4 +-
 .../notifications/NotificationWizardBuilder.java   |  12 +-
 .../syncope/client/console/panels/AnyPanel.java    |   6 +-
 .../syncope/client/console/panels/BeanPanel.java   |  22 +--
 .../console/panels/DelegationDirectoryPanel.java   |  18 ++-
 .../console/panels/DelegationSelectionPanel.java   |  28 ++--
 .../client/console/panels/DirectoryPanel.java      |  10 +-
 .../client/console/panels/DynRealmModalPanel.java  |  36 ++---
 .../client/console/panels/RealmChoicePanel.java    |  13 +-
 .../syncope/client/console/panels/TogglePanel.java |   8 +-
 .../console/panels/search/AbstractSearchPanel.java |  73 +++++++--
 .../panels/search/AnyObjectSearchPanel.java        |  17 +-
 .../client/console/panels/search/FIQLQueries.java  |  53 +++++++
 .../panels/search/FIQLQueryDirectoryPanel.java     | 176 +++++++++++++++++++++
 .../console/panels/search/GroupSearchPanel.java    |  17 +-
 .../console/panels/search/SaveFIQLQuery.java       |  65 +++++---
 .../console/panels/search/SearchClausePanel.java   |  16 +-
 .../client/console/panels/search/SearchUtils.java  |   2 +-
 .../console/panels/search/UserSearchPanel.java     |  17 +-
 .../console/policies/PolicyRuleWizardBuilder.java  |   4 +-
 .../console/reports/ReportDirectoryPanel.java      |   2 +-
 .../console/reports/ReportletDirectoryPanel.java   |  10 +-
 .../console/reports/ReportletWizardBuilder.java    |  10 +-
 .../client/console/rest/FIQLQueryRestClient.java   |  48 ++++++
 .../console/tasks/AbstractPropagationTasks.java    |   1 -
 .../client/console/tasks/AnyPropagationTasks.java  |   2 +-
 .../client/console/tasks/PushTaskFilters.java      |  23 +--
 .../console/tasks/SchedTaskWizardBuilder.java      |   8 +-
 .../markup/html/form/ActionLinksTogglePanel.java   |   2 +-
 .../console/wizards/UserSelectionWizardStep.java   |   2 +-
 .../console/wizards/any/AnyWizardBuilder.java      |   2 +-
 .../console/wizards/any/DynamicMemberships.java    |   7 +-
 .../client/console/wizards/any/Ownership.java      |  49 +++---
 .../client/console/wizards/any/Relationships.java  |   3 +-
 .../console/wizards/role/RoleWizardBuilder.java    |  28 ++--
 .../console/src/main/resources/console.properties  |   2 +
 .../console/panels/search/AbstractSearchPanel.html |   5 +
 .../{AbstractSearchPanel.html => FIQLQueries.html} |  17 +-
 .../console/panels/search/FIQLQueries.properties   |  17 ++
 .../panels/search/FIQLQueries_fr_CA.properties     |  17 ++
 .../panels/search/FIQLQueries_it.properties        |  18 +++
 .../panels/search/FIQLQueries_ja.properties        |  17 ++
 .../panels/search/FIQLQueries_pt_BR.properties     |  18 +++
 .../panels/search/FIQLQueries_ru.properties        |  18 +++
 ...AbstractSearchPanel.html => SaveFIQLQuery.html} |  24 +--
 .../console/panels/search/SaveFIQLQuery.properties |  17 ++
 .../panels/search/SaveFIQLQuery_fr_CA.properties   |  17 ++
 .../panels/search/SaveFIQLQuery_it.properties      |  18 +++
 .../panels/search/SaveFIQLQuery_ja.properties      |  17 ++
 .../panels/search/SaveFIQLQuery_pt_BR.properties   |  18 +++
 .../panels/search/SaveFIQLQuery_ru.properties      |  18 +++
 .../console/panels/search/SearchClausePanel.html   |   2 +-
 .../syncope/client/console/AbstractTest.java       |  16 ++
 .../console/panels/search/UserSearchPanelTest.java |  11 +-
 .../syncope/client/enduser/EnduserProperties.java  |  10 --
 .../apache/syncope/common/lib/to/FIQLQueryTO.java  | 107 +++++++++++++
 .../common/rest/api/service/FIQLQueryService.java  | 124 +++++++++++++++
 .../apache/syncope/core/logic/DelegationLogic.java |   1 -
 .../apache/syncope/core/logic/FIQLQueryLogic.java  | 143 +++++++++++++++++
 .../syncope/core/logic/IdRepoLogicContext.java     |  12 ++
 .../core/rest/cxf/IdRepoRESTCXFContext.java        |   9 ++
 .../rest/cxf/service/FIQLQueryServiceImpl.java     |  67 ++++++++
 .../core/persistence/api/dao/DelegationDAO.java    |   2 +
 .../dao/{DelegationDAO.java => FIQLQueryDAO.java}  |  22 +--
 .../persistence/api/dao/PushCorrelationRule.java   |   2 +-
 .../core/persistence/api/entity/FIQLQuery.java     |  23 ++-
 .../persistence/jpa/JPAJSONPersistenceContext.java |   3 +
 .../core/persistence/jpa/dao/JPAJSONUserDAO.java   |   3 +
 .../core/persistence/jpa/PersistenceContext.java   |  12 +-
 .../jpa/dao/DefaultPushCorrelationRule.java        |   2 +-
 .../core/persistence/jpa/dao/JPADelegationDAO.java |   7 +-
 .../core/persistence/jpa/dao/JPAFIQLQueryDAO.java  |  79 +++++++++
 .../core/persistence/jpa/dao/JPAUserDAO.java       |  22 ++-
 .../core/persistence/jpa/entity/JPADelegation.java |   4 +-
 .../persistence/jpa/entity/JPAEntityFactory.java   |   3 +
 .../core/persistence/jpa/entity/JPAFIQLQuery.java  |  91 +++++++++++
 .../provisioning/api/data/FIQLQueryDataBinder.java |  14 +-
 .../provisioning/java/ProvisioningContext.java     |  12 ++
 .../java/data/FIQLQueryDataBinderImpl.java         |  91 +++++++++++
 .../java/pushpull/OutboundMatcher.java             |   2 +-
 .../client/console/panels/NewBpmnProcess.java      |   6 +-
 .../apache/syncope/fit/core/FIQLQueryITCase.java   |  87 ++++++++++
 pom.xml                                            |   4 +-
 95 files changed, 1875 insertions(+), 380 deletions(-)

diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileWizardBuilder.java b/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileWizardBuilder.java
index ae9bf98a3b..08475c4110 100644
--- a/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileWizardBuilder.java
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/authprofiles/AuthProfileWizardBuilder.java
@@ -66,7 +66,7 @@ public abstract class AuthProfileWizardBuilder<T extends BaseBean> extends BaseA
         Step(final T modelObject) {
             model.setObject(modelObject);
             model.setInitialModelObject(modelObject);
-            add(new BeanPanel<>("bean", model).setRenderBodyOnly(true));
+            add(new BeanPanel<>("bean", model, pageRef).setRenderBodyOnly(true));
         }
     }
 }
diff --git a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder.java b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder.java
index 6bc14cb222..784b04d5db 100644
--- a/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder.java
+++ b/client/am/console/src/main/java/org/apache/syncope/client/console/wizards/AttrRepoWizardBuilder.java
@@ -155,12 +155,12 @@ public class AttrRepoWizardBuilder extends BaseAjaxWizardBuilder<AttrRepoTO> {
         }
     }
 
-    protected static class Configuration extends WizardStep {
+    protected class Configuration extends WizardStep {
 
         private static final long serialVersionUID = -785981096328637758L;
 
         Configuration(final AttrRepoTO attrRepo) {
-            add(new BeanPanel<>("bean", new PropertyModel<>(attrRepo, "conf")).setRenderBodyOnly(true));
+            add(new BeanPanel<>("bean", new PropertyModel<>(attrRepo, "conf"), pageRef).setRenderBodyOnly(true));
         }
     }
 
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 e4103e6a79..d976f5ab8a 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
@@ -160,16 +160,17 @@ public class AuthModuleWizardBuilder extends BaseAjaxWizardBuilder<AuthModuleTO>
         }
     }
 
-    protected static class Configuration extends WizardStep {
+    protected class Configuration extends WizardStep {
 
         private static final long serialVersionUID = -785981096328637758L;
 
         Configuration(final AuthModuleTO authModule) {
-            add(new BeanPanel<>("bean", new PropertyModel<>(authModule, "conf"), "ldap").setRenderBodyOnly(true));
+            add(new BeanPanel<>(
+                    "bean", new PropertyModel<>(authModule, "conf"), pageRef, "ldap").setRenderBodyOnly(true));
         }
     }
 
-    protected static class GoogleMfaAuthModuleConfLDAP extends WizardStep implements WizardModel.ICondition {
+    protected class GoogleMfaAuthModuleConfLDAP extends WizardStep implements WizardModel.ICondition {
 
         private static final long serialVersionUID = 5328049907748683944L;
 
@@ -213,7 +214,7 @@ public class AuthModuleWizardBuilder extends BaseAjaxWizardBuilder<AuthModuleTO>
             });
             add(enable);
 
-            add(new BeanPanel<>("bean", beanPanelModel).setRenderBodyOnly(true));
+            add(new BeanPanel<>("bean", beanPanelModel, pageRef).setRenderBodyOnly(true));
             setOutputMarkupId(true);
         }
 
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjectListViewPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjectListViewPanel.java
index f06eacb6ab..24875279f1 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjectListViewPanel.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjectListViewPanel.java
@@ -79,21 +79,23 @@ public abstract class ConnObjectListViewPanel extends Panel {
 
     private static final long serialVersionUID = 4986172040062752781L;
 
-    private static final Logger LOG = LoggerFactory.getLogger(ConnObjectListViewPanel.class);
+    protected static final Logger LOG = LoggerFactory.getLogger(ConnObjectListViewPanel.class);
 
-    private static final int SIZE = 10;
+    protected static final int SIZE = 10;
 
-    private static final String STATUS = "Status";
+    protected static final String STATUS = "Status";
 
-    private String nextPageCookie;
+    protected String nextPageCookie;
 
-    private AbstractSearchPanel searchPanel;
+    protected AbstractSearchPanel searchPanel;
 
-    private WebMarkupContainer arrows;
+    protected WebMarkupContainer arrows;
 
-    private String anyType;
+    protected String anyType;
 
-    private ResourceTO resource;
+    protected ResourceTO resource;
+
+    protected final PageReference pageRef;
 
     protected ConnObjectListViewPanel(
             final String id,
@@ -105,6 +107,7 @@ public abstract class ConnObjectListViewPanel extends Panel {
 
         this.anyType = anyType;
         this.resource = resource;
+        this.pageRef = pageRef;
 
         final Model<Integer> model = Model.of(-1);
         final StringResourceModel res = new StringResourceModel("search.result", this, new Model<>(anyType));
@@ -148,7 +151,7 @@ public abstract class ConnObjectListViewPanel extends Panel {
         List<ConnObjectTO> listOfItems = reloadItems(resource.getKey(), anyType, null, null);
 
         ListViewPanel.Builder<ConnObjectTO> builder = new ListViewPanel.Builder<>(
-            ConnObjectTO.class, pageRef) {
+                ConnObjectTO.class, pageRef) {
 
             private static final long serialVersionUID = -8251750413385566738L;
 
@@ -158,45 +161,45 @@ public abstract class ConnObjectListViewPanel extends Panel {
                     ReconStatus status;
                     try {
                         status = ReconciliationRestClient.status(
-                            new ReconQuery.Builder(anyType, resource.getKey()).fiql(bean.getFiql()).build());
+                                new ReconQuery.Builder(anyType, resource.getKey()).fiql(bean.getFiql()).build());
                     } catch (Exception e) {
                         LOG.error("While requesting for reconciliation status of {} {} with FIQL '{}'",
-                            anyType, resource.getKey(), bean.getFiql(), e);
+                                anyType, resource.getKey(), bean.getFiql(), e);
 
                         status = new ReconStatus();
                     }
 
                     return status.getOnSyncope() == null
-                        ? StatusUtils.getLabel("field", "notfound icon", "Not found", Constants.NOT_FOUND_ICON)
-                        : new Label("field", Model.of()).add(new PopoverBehavior(
-                        Model.of(),
-                        Model.of(status.getAnyKey()),
-                        new PopoverConfig().
-                            withTitle(status.getMatchType() == MatchType.LINKED_ACCOUNT
-                                ? MatchType.LINKED_ACCOUNT.name() + ", " + AnyTypeKind.USER
-                                : status.getAnyTypeKind().name()).
-                            withPlacement(TooltipConfig.Placement.left)) {
-
-                        private static final long serialVersionUID = -7867802555691605021L;
-
-                        @Override
-                        protected String createRelAttribute() {
-                            return "field";
-                        }
-
-                        @Override
-                        public void onComponentTag(final Component component, final ComponentTag tag) {
-                            super.onComponentTag(component, tag);
-                            tag.put("class", Constants.ACTIVE_ICON);
-                        }
-                    });
+                            ? StatusUtils.getLabel("field", "notfound icon", "Not found", Constants.NOT_FOUND_ICON)
+                            : new Label("field", Model.of()).add(new PopoverBehavior(
+                                    Model.of(),
+                                    Model.of(status.getAnyKey()),
+                                    new PopoverConfig().
+                                            withTitle(status.getMatchType() == MatchType.LINKED_ACCOUNT
+                                                    ? MatchType.LINKED_ACCOUNT.name() + ", " + AnyTypeKind.USER
+                                                    : status.getAnyTypeKind().name()).
+                                            withPlacement(TooltipConfig.Placement.left)) {
+
+                                private static final long serialVersionUID = -7867802555691605021L;
+
+                                @Override
+                                protected String createRelAttribute() {
+                                    return "field";
+                                }
+
+                                @Override
+                                public void onComponentTag(final Component component, final ComponentTag tag) {
+                                    super.onComponentTag(component, tag);
+                                    tag.put("class", Constants.ACTIVE_ICON);
+                                }
+                            });
                 } else {
                     Optional<Attr> attr =
-                        bean.getAttrs().stream().filter(object -> object.getSchema().equals(key)).findAny();
+                            bean.getAttrs().stream().filter(object -> object.getSchema().equals(key)).findAny();
 
                     return attr.isEmpty() || attr.get().getValues().isEmpty()
-                        ? new Label("field", StringUtils.EMPTY)
-                        : new CollectionPanel("field", attr.get().getValues());
+                            ? new Label("field", StringUtils.EMPTY)
+                            : new CollectionPanel("field", attr.get().getValues());
                 }
             }
 
@@ -228,19 +231,19 @@ public abstract class ConnObjectListViewPanel extends Panel {
                 public void onClick(final AjaxRequestTarget target, final ConnObjectTO modelObject) {
                     try {
                         ReconStatus status = ReconciliationRestClient.status(
-                            new ReconQuery.Builder(anyType, resource.getKey()).fiql(modelObject.getFiql()).build());
+                                new ReconQuery.Builder(anyType, resource.getKey()).fiql(modelObject.getFiql()).build());
 
                         pullConnObject(
-                            modelObject.getFiql(),
-                            target,
-                            resource.getKey(),
-                            anyType,
-                            status.getRealm(),
-                            StringUtils.isNotBlank(status.getAnyKey()),
-                            pageRef);
+                                modelObject.getFiql(),
+                                target,
+                                resource.getKey(),
+                                anyType,
+                                status.getRealm(),
+                                StringUtils.isNotBlank(status.getAnyKey()),
+                                pageRef);
                     } catch (Exception e) {
                         LOG.error("While puling single object {} {} with FIQL '{}'",
-                            anyType, resource.getKey(), modelObject.getFiql(), e);
+                                anyType, resource.getKey(), modelObject.getFiql(), e);
 
                         SyncopeConsoleSession.get().onException(e);
                         ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
@@ -298,13 +301,14 @@ public abstract class ConnObjectListViewPanel extends Panel {
             boolean isOnSyncope,
             PageReference pageRef);
 
-    private List<ConnObjectTO> reloadItems(
+    protected List<ConnObjectTO> reloadItems(
             final String resource,
             final String anyType,
             final String cookie,
             final String fiql) {
 
-        Pair<String, List<ConnObjectTO>> items = new ResourceRestClient().searchConnObjects(resource,
+        Pair<String, List<ConnObjectTO>> items = new ResourceRestClient().searchConnObjects(
+                resource,
                 anyType,
                 new ConnObjectTOQuery.Builder().
                         size(SIZE).
@@ -316,7 +320,7 @@ public abstract class ConnObjectListViewPanel extends Panel {
         return items.getRight();
     }
 
-    private AbstractSearchPanel getSearchPanel(final String id, final String anyType) {
+    protected AbstractSearchPanel getSearchPanel(final String id, final String anyType) {
         final List<SearchClause> clauses = new ArrayList<>();
         final SearchClause clause = new SearchClause();
         clauses.add(clause);
@@ -331,10 +335,10 @@ public abstract class ConnObjectListViewPanel extends Panel {
                 : AnyTypeRestClient.read(anyType).getKind();
 
         return new ConnObjectSearchPanel.Builder(resource, anyTypeKind, anyType,
-                new ListModel<>(clauses)).required(true).enableSearch().build(id);
+                new ListModel<>(clauses), pageRef).required(true).enableSearch().build(id);
     }
 
-    private String getFiql() {
+    protected String getFiql() {
         return SearchUtils.buildFIQL(
                 searchPanel.getModel().getObject(),
                 SyncopeClient.getConnObjectTOFiqlSearchConditionBuilder(),
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/search/ConnObjectSearchPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/search/ConnObjectSearchPanel.java
index 2d69576229..3dc4f855e5 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/search/ConnObjectSearchPanel.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/search/ConnObjectSearchPanel.java
@@ -23,10 +23,13 @@ import java.util.Map;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 import org.apache.syncope.client.console.rest.ConnectorRestClient;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.search.AbstractFiqlSearchConditionBuilder;
 import org.apache.syncope.common.lib.to.ConnIdObjectClassTO;
 import org.apache.syncope.common.lib.to.PlainSchemaTO;
 import org.apache.syncope.common.lib.to.ResourceTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.wicket.PageReference;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.LoadableDetachableModel;
 
@@ -52,9 +55,10 @@ public class ConnObjectSearchPanel extends AbstractSearchPanel {
                 final ResourceTO resource,
                 final AnyTypeKind anyType,
                 final String type,
-                final IModel<List<SearchClause>> model) {
+                final IModel<List<SearchClause>> model,
+                final PageReference pageRef) {
 
-            super(model);
+            super(model, pageRef);
             this.resource = resource;
             this.anyType = anyType;
             this.typeName = type;
@@ -76,6 +80,16 @@ public class ConnObjectSearchPanel extends AbstractSearchPanel {
         this.resource = builder.resource;
     }
 
+    @Override
+    protected AbstractFiqlSearchConditionBuilder<?, ?, ?> getSearchConditionBuilder() {
+        return SyncopeClient.getConnObjectTOFiqlSearchConditionBuilder();
+    }
+
+    @Override
+    protected String getFIQLQueryTarget() {
+        return "CONN_OBJ";
+    }
+
     @Override
     protected void populate() {
         this.types = new LoadableDetachableModel<>() {
@@ -105,11 +119,11 @@ public class ConnObjectSearchPanel extends AbstractSearchPanel {
             @Override
             protected Map<String, PlainSchemaTO> load() {
                 return connectorRestClient.buildObjectClassInfo(
-                    connectorRestClient.read(resource.getConnector()), false).stream().
-                    map(ConnIdObjectClassTO::getAttributes).
-                    flatMap(List::stream).
-                    collect(Collectors.toMap(PlainSchemaTO::getKey, Function.identity(),
-                        (schema1, schema2) -> schema1));
+                        connectorRestClient.read(resource.getConnector()), false).stream().
+                        map(ConnIdObjectClassTO::getAttributes).
+                        flatMap(List::stream).
+                        collect(Collectors.toMap(PlainSchemaTO::getKey, Function.identity(),
+                                (schema1, schema2) -> schema1));
             }
         };
     }
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java
index 1a85a910e5..8ddf0bebf9 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java
@@ -23,7 +23,6 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal;
 import java.io.Serializable;
 import java.text.MessageFormat;
 import java.util.Optional;
-import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
 import org.apache.syncope.client.console.audit.AuditHistoryModal;
 import org.apache.syncope.client.console.panels.ConnObjects;
@@ -684,7 +683,7 @@ public class TopologyTogglePanel extends TogglePanel<Serializable> {
             if (result != null && result instanceof ConnInstanceTO && target.isPresent()) {
                 // update Toggle Panel header
                 ConnInstanceTO conn = ConnInstanceTO.class.cast(result);
-                setHeader(target.get(), StringUtils.abbreviate(conn.getDisplayName(), HEADER_FIRST_ABBREVIATION));
+                setHeader(target.get(), conn.getDisplayName());
             }
         }
     }
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsSearchPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsSearchPanel.java
index 3f76ad6a70..19d8ac2558 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsSearchPanel.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsSearchPanel.java
@@ -72,7 +72,8 @@ public class MergeLinkedAccountsSearchPanel extends WizardStep {
 
         userSearchFragment = new Fragment("search", "userSearchFragment", this);
         userSearchPanel = UserSearchPanel.class.cast(new UserSearchPanel.Builder(
-                new ListModel<>(new ArrayList<>())).required(false).enableSearch(MergeLinkedAccountsSearchPanel.this).
+                new ListModel<>(new ArrayList<>()), pageRef).
+                required(false).enableSearch(MergeLinkedAccountsSearchPanel.this).
                 build("usersearch"));
         userSearchFragment.add(userSearchPanel);
 
@@ -88,17 +89,17 @@ public class MergeLinkedAccountsSearchPanel extends WizardStep {
     @Override
     public void onEvent(final IEvent<?> event) {
         if (event.getPayload() instanceof SearchClausePanel.SearchEvent) {
-            final AjaxRequestTarget target = SearchClausePanel.SearchEvent.class.cast(event.getPayload()).getTarget();
-            final String fiql = "username!~" + this.wizardModel.getBaseUser().getUsername() + ';'
+            AjaxRequestTarget target = SearchClausePanel.SearchEvent.class.cast(event.getPayload()).getTarget();
+            String fiql = "username!~" + wizardModel.getBaseUser().getUsername() + ';'
                     + SearchUtils.buildFIQL(userSearchPanel.getModel().getObject(),
                             SyncopeClient.getUserSearchConditionBuilder());
             userDirectoryPanel.search(fiql, target);
         } else if (event.getPayload() instanceof AnySelectionDirectoryPanel.ItemSelection) {
-            AnySelectionDirectoryPanel.ItemSelection payload =
+            AnySelectionDirectoryPanel.ItemSelection<?> payload =
                     (AnySelectionDirectoryPanel.ItemSelection) event.getPayload();
 
-            final AnyTO sel = payload.getSelection();
-            this.wizardModel.setMergingUser(new UserRestClient().read(sel.getKey()));
+            AnyTO sel = payload.getSelection();
+            wizardModel.setMergingUser(new UserRestClient().read(sel.getKey()));
 
             String tableId = ((Component) event.getSource()).
                     get("container:content:searchContainer:resultTable:tablePanel:groupForm:checkgroup:dataTable").
diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/CommonUIProperties.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/CommonUIProperties.java
index cd47af7a8d..8e24a69c2b 100644
--- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/CommonUIProperties.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/CommonUIProperties.java
@@ -24,6 +24,8 @@ import org.apache.syncope.common.lib.SyncopeProperties;
 
 public abstract class CommonUIProperties extends SyncopeProperties {
 
+    private String adminUser = "admin";
+
     private boolean xForward = true;
 
     private String xForwardProtocolHeader = "X-Forwarded-Proto";
@@ -40,6 +42,14 @@ public abstract class CommonUIProperties extends SyncopeProperties {
 
     private final Map<String, String> securityHeaders = new HashMap<>();
 
+    public String getAdminUser() {
+        return adminUser;
+    }
+
+    public void setAdminUser(final String adminUser) {
+        this.adminUser = adminUser;
+    }
+
     public boolean isxForward() {
         return xForward;
     }
diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AbstractFieldPanel.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AbstractFieldPanel.java
index 30dfe7f908..037f64537f 100644
--- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AbstractFieldPanel.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AbstractFieldPanel.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.client.ui.commons.markup.html.form;
 
+import java.util.Optional;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.wicket.Component;
 import org.apache.wicket.markup.html.basic.Label;
@@ -61,17 +62,12 @@ public abstract class AbstractFieldPanel<T> extends Panel {
     }
 
     public AbstractFieldPanel<T> hideLabel() {
-        final Component label = get(LABEL);
-
-        if (label != null) {
-            label.setVisible(false);
-        }
-
+        Optional.ofNullable(get(LABEL)).ifPresent(label -> label.setVisible(false));
         return this;
     }
 
     public AbstractFieldPanel<T> showExternAction(final Component component) {
-        final Fragment fragment = new Fragment("externalAction", "externalActionFragment", AbstractFieldPanel.this);
+        Fragment fragment = new Fragment("externalAction", "externalActionFragment", AbstractFieldPanel.this);
         addOrReplace(fragment);
         fragment.add(component.setRenderBodyOnly(false));
         return this;
@@ -90,7 +86,7 @@ public abstract class AbstractFieldPanel<T> extends Panel {
             setRequired(true);
         }
 
-        final Fragment fragment = new Fragment("required", "requiredFragment", this);
+        Fragment fragment = new Fragment("required", "requiredFragment", this);
         fragment.add(new Label("requiredLabel", "*"));
         replace(fragment);
 
@@ -104,9 +100,7 @@ public abstract class AbstractFieldPanel<T> extends Panel {
             setRequired(false);
         }
 
-        final Fragment fragment = new Fragment("required", "emptyFragment", this);
-
-        replace(fragment);
+        replace(new Fragment("required", "emptyFragment", this));
 
         this.isRequiredLabelAdded = false;
 
diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AbstractMultiPanel.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AbstractMultiPanel.java
index 5e45a5600c..1e317eeea4 100644
--- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AbstractMultiPanel.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/AbstractMultiPanel.java
@@ -65,8 +65,7 @@ public abstract class AbstractMultiPanel<INNER> extends AbstractFieldPanel<List<
 
         view = new InnerView("view", name, model);
 
-        List<INNER> obj = model.getObject();
-        if (CollectionUtils.isEmpty(obj)) {
+        if (CollectionUtils.isEmpty(model.getObject())) {
             form.addOrReplace(getNoDataFragment(model, name));
         } else {
             form.addOrReplace(getDataFragment());
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/ConsoleProperties.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/ConsoleProperties.java
index d2ef41933b..30764ebf2f 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/ConsoleProperties.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/ConsoleProperties.java
@@ -61,6 +61,8 @@ public class ConsoleProperties extends CommonUIProperties {
         }
     }
 
+    private String adminUser = "admin";
+
     private String reconciliationReportKey;
 
     private final Map<String, Class<? extends BasePage>> page = new HashMap<>();
@@ -69,6 +71,16 @@ public class ConsoleProperties extends CommonUIProperties {
 
     private final Topology topology = new Topology();
 
+    @Override
+    public String getAdminUser() {
+        return adminUser;
+    }
+
+    @Override
+    public void setAdminUser(final String adminUser) {
+        this.adminUser = adminUser;
+    }
+
     public String getReconciliationReportKey() {
         return reconciliationReportKey;
     }
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 6919048c6e..2bb4daead5 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
@@ -261,6 +261,10 @@ public class SyncopeWebApplication extends WicketBootSecuredWebApplication {
         return props.getDefaultAnyPanelClass();
     }
 
+    public String getAdminUser() {
+        return props.getAdminUser();
+    }
+
     public String getAnonymousUser() {
         return props.getAnonymousUser();
     }
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/IdRepoConstants.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/IdRepoConstants.java
index ea418f725d..ff5b475e59 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/IdRepoConstants.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/IdRepoConstants.java
@@ -82,9 +82,9 @@ public final class IdRepoConstants {
 
     public static final String PREF_PROPAGATION_TASKS_PAGINATOR_ROWS = "proagationtasks.paginator.rows";
 
-    public static final String PREF_REPORT_TASKS_PAGINATOR_ROWS = "report.paginator.rows";
+    public static final String PREF_REPORTLET_PAGINATOR_ROWS = "reportlet.paginator.rows";
 
-    public static final String PREF_REPORTLET_TASKS_PAGINATOR_ROWS = "reportlet.paginator.rows";
+    public static final String PREF_FIQL_QUERIES_PAGINATOR_ROWS = "reportlet.paginator.rows";
 
     public static final String PREF_POLICY_RULE_PAGINATOR_ROWS = "policy.rules.paginator.rows";
 
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 f13fd11378..c915198ebc 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
@@ -183,7 +183,7 @@ public class NotificationWizardBuilder extends BaseAjaxWizardBuilder<Notificatio
         }
     }
 
-    public static class About extends Panel {
+    public class About extends Panel {
 
         private static final long serialVersionUID = -9149543787708482882L;
 
@@ -252,15 +252,15 @@ public class NotificationWizardBuilder extends BaseAjaxWizardBuilder<Notificatio
 
             switch (type) {
                 case "USER":
-                    clause = new UserSearchPanel.Builder(clauseModel);
+                    clause = new UserSearchPanel.Builder(clauseModel, pageRef);
                     break;
 
                 case "GROUP":
-                    clause = new GroupSearchPanel.Builder(clauseModel);
+                    clause = new GroupSearchPanel.Builder(clauseModel, pageRef);
                     break;
 
                 default:
-                    clause = new AnyObjectSearchPanel.Builder(type, clauseModel);
+                    clause = new AnyObjectSearchPanel.Builder(type, clauseModel, pageRef);
             }
 
             return clause;
@@ -321,7 +321,7 @@ public class NotificationWizardBuilder extends BaseAjaxWizardBuilder<Notificatio
         }
     }
 
-    public static class Recipients extends WizardStep {
+    public class Recipients extends WizardStep {
 
         private static final long serialVersionUID = -7709805590497687958L;
 
@@ -363,7 +363,7 @@ public class NotificationWizardBuilder extends BaseAjaxWizardBuilder<Notificatio
                     build("staticRecipients", "staticRecipients", staticRecipientsFieldPanel).hideLabel());
 
             add(new UserSearchPanel.Builder(
-                    new PropertyModel<>(modelObject, "recipientClauses")).
+                    new PropertyModel<>(modelObject, "recipientClauses"), pageRef).
                     required(false).build("recipients"));
 
             AjaxDropDownChoicePanel<String> recipientsProvider = new AjaxDropDownChoicePanel<>(
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AnyPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AnyPanel.java
index ec9590ab6a..adfbf00cd6 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AnyPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/AnyPanel.java
@@ -314,7 +314,7 @@ public class AnyPanel extends Panel implements ModalPanel {
                 clause.setProperty(Constants.USERNAME_FIELD_NAME);
 
                 panel = new UserSearchPanel.Builder(
-                        new ListModel<>(clauses)).required(true).enableSearch().build(id);
+                        new ListModel<>(clauses), pageRef).required(true).enableSearch().build(id);
                 break;
 
             case GROUP:
@@ -323,7 +323,7 @@ public class AnyPanel extends Panel implements ModalPanel {
                 clause.setProperty(Constants.NAME_FIELD_NAME);
 
                 panel = new GroupSearchPanel.Builder(
-                        new ListModel<>(clauses)).required(true).enableSearch().build(id);
+                        new ListModel<>(clauses), pageRef).required(true).enableSearch().build(id);
                 break;
 
             case ANY_OBJECT:
@@ -332,7 +332,7 @@ public class AnyPanel extends Panel implements ModalPanel {
                 clause.setProperty(Constants.NAME_FIELD_NAME);
 
                 panel = new AnyObjectSearchPanel.Builder(anyTypeTO.getKey(),
-                        new ListModel<>(clauses)).required(true).enableSearch().build(id);
+                        new ListModel<>(clauses), pageRef).required(true).enableSearch().build(id);
                 break;
 
             default:
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 70f5fb94d3..9742abb68b 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
@@ -56,6 +56,7 @@ import org.apache.syncope.common.lib.search.AbstractFiqlSearchConditionBuilder;
 import org.apache.syncope.common.lib.to.EntityTO;
 import org.apache.syncope.common.lib.to.SchemaTO;
 import org.apache.syncope.common.lib.types.SchemaType;
+import org.apache.wicket.PageReference;
 import org.apache.wicket.core.util.lang.PropertyResolver;
 import org.apache.wicket.core.util.lang.PropertyResolverConverter;
 import org.apache.wicket.markup.html.basic.Label;
@@ -84,14 +85,15 @@ public class BeanPanel<T extends Serializable> extends Panel {
 
     private final Map<String, Pair<AbstractFiqlSearchConditionBuilder<?, ?, ?>, List<SearchClause>>> sCondWrapper;
 
-    public BeanPanel(final String id, final IModel<T> bean, final String... excluded) {
-        this(id, bean, null, excluded);
+    public BeanPanel(final String id, final IModel<T> bean, final PageReference pageRef, final String... excluded) {
+        this(id, bean, null, pageRef, excluded);
     }
 
     public BeanPanel(
             final String id,
             final IModel<T> bean,
             final Map<String, Pair<AbstractFiqlSearchConditionBuilder<?, ?, ?>, List<SearchClause>>> sCondWrapper,
+            final PageReference pageRef,
             final String... excluded) {
 
         super(id, bean);
@@ -153,32 +155,30 @@ public class BeanPanel<T extends Serializable> extends Panel {
                     switch (scondAnnot.type()) {
                         case "USER":
                             panel = new UserSearchPanel.Builder(
-                                    new ListModel<>(clauses)).required(false).build("value");
+                                    new ListModel<>(clauses), pageRef).required(false).build("value");
                             builder = SyncopeClient.getUserSearchConditionBuilder();
                             break;
 
                         case "GROUP":
                             panel = new GroupSearchPanel.Builder(
-                                    new ListModel<>(clauses)).required(false).build("value");
+                                    new ListModel<>(clauses), pageRef).required(false).build("value");
                             builder = SyncopeClient.getGroupSearchConditionBuilder();
                             break;
 
                         default:
                             panel = new AnyObjectSearchPanel.Builder(
                                     scondAnnot.type(),
-                                    new ListModel<>(clauses)).required(false).build("value");
-                            builder = SyncopeClient.getAnyObjectSearchConditionBuilder(null);
+                                    new ListModel<>(clauses), pageRef).required(false).build("value");
+                            builder = SyncopeClient.getAnyObjectSearchConditionBuilder(scondAnnot.type());
                     }
 
                     if (BeanPanel.this.sCondWrapper != null) {
                         BeanPanel.this.sCondWrapper.put(fieldName, Pair.of(builder, clauses));
                     }
                 } else if (List.class.equals(field.getType())) {
-                    Class<?> listItemType = String.class;
-                    if (field.getGenericType() instanceof ParameterizedType) {
-                        listItemType = (Class<?>) ((ParameterizedType) field.getGenericType()).
-                                getActualTypeArguments()[0];
-                    }
+                    Class<?> listItemType = field.getGenericType() instanceof ParameterizedType
+                            ? (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]
+                            : String.class;
 
                     if (listItemType.equals(String.class) && schemaAnnot != null) {
                         List<SchemaTO> choices = new ArrayList<>();
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DelegationDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DelegationDirectoryPanel.java
index 7297e441ce..fab7e51f38 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DelegationDirectoryPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DelegationDirectoryPanel.java
@@ -97,11 +97,13 @@ public class DelegationDirectoryPanel extends
 
         columns.add(new AbstractColumn<>(new ResourceModel("delegating"), "delegating") {
 
+            private static final long serialVersionUID = -7835464045129401360L;
+
             @Override
             public void populateItem(
-                final Item<ICellPopulator<DelegationTO>> cellItem,
-                final String componentId,
-                final IModel<DelegationTO> rowModel) {
+                    final Item<ICellPopulator<DelegationTO>> cellItem,
+                    final String componentId,
+                    final IModel<DelegationTO> rowModel) {
 
                 String delegating = rowModel.getObject().getDelegating();
                 if (SyncopeConsoleSession.get().owns(IdRepoEntitlement.USER_READ)) {
@@ -116,11 +118,13 @@ public class DelegationDirectoryPanel extends
 
         columns.add(new AbstractColumn<>(new ResourceModel("delegated"), "delegated") {
 
+            private static final long serialVersionUID = -7835464045129401360L;
+
             @Override
             public void populateItem(
-                final Item<ICellPopulator<DelegationTO>> cellItem,
-                final String componentId,
-                final IModel<DelegationTO> rowModel) {
+                    final Item<ICellPopulator<DelegationTO>> cellItem,
+                    final String componentId,
+                    final IModel<DelegationTO> rowModel) {
 
                 String delegated = rowModel.getObject().getDelegated();
                 if (SyncopeConsoleSession.get().owns(IdRepoEntitlement.USER_READ)) {
@@ -153,7 +157,7 @@ public class DelegationDirectoryPanel extends
             @Override
             public void onClick(final AjaxRequestTarget target, final DelegationTO ignore) {
                 send(DelegationDirectoryPanel.this, Broadcast.EXACT,
-                    new AjaxWizard.EditItemActionEvent<>(model.getObject(), target));
+                        new AjaxWizard.EditItemActionEvent<>(model.getObject(), target));
             }
         }, ActionLink.ActionType.EDIT, StringUtils.EMPTY);
         panel.add(new ActionLink<>() {
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DelegationSelectionPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DelegationSelectionPanel.java
index 4d89c84003..177430975a 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DelegationSelectionPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DelegationSelectionPanel.java
@@ -34,22 +34,24 @@ public class DelegationSelectionPanel extends Panel {
         super(id);
 
         IndicatingOnConfirmAjaxLink<String> link =
-            new IndicatingOnConfirmAjaxLink<>("link", "confirmDelegation", true) {
+                new IndicatingOnConfirmAjaxLink<>("link", "confirmDelegation", true) {
 
-                @Override
-                public void onClick(final AjaxRequestTarget target) {
-                    SyncopeConsoleSession.get().setDelegatedBy(delegating);
-                    setResponsePage(Dashboard.class);
-                }
+            private static final long serialVersionUID = 6611857585742411796L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target) {
+                SyncopeConsoleSession.get().setDelegatedBy(delegating);
+                setResponsePage(Dashboard.class);
+            }
 
-                @Override
-                protected void onComponentTag(final ComponentTag tag) {
-                    super.onComponentTag(tag);
-                    if (delegating.equals(SyncopeConsoleSession.get().getDelegatedBy())) {
-                        tag.append("class", "disabled", " ");
-                    }
+            @Override
+            protected void onComponentTag(final ComponentTag tag) {
+                super.onComponentTag(tag);
+                if (delegating.equals(SyncopeConsoleSession.get().getDelegatedBy())) {
+                    tag.append("class", "disabled", " ");
                 }
-            };
+            }
+        };
         add(link);
         link.setOutputMarkupId(true);
         link.add(new Label("label", delegating));
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DirectoryPanel.java
index 3f1692e6aa..dabfeda6c5 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DirectoryPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DirectoryPanel.java
@@ -66,7 +66,7 @@ public abstract class DirectoryPanel<
     /**
      * Number of rows per page.
      */
-    protected int rows;
+    protected Integer rows;
 
     /**
      * Container used to refresh table.
@@ -274,8 +274,8 @@ public abstract class DirectoryPanel<
     protected void updateResultTable(final boolean create, final int rows) {
         dataProvider = dataProvider();
 
-        final int currentPage = Optional.ofNullable(resultTable)
-                .map(table -> (create ? (int) table.getPageCount() - 1 : (int) table.getCurrentPage())).orElse(0);
+        int currentPage = Optional.ofNullable(resultTable).
+                map(table -> (create ? (int) table.getPageCount() - 1 : (int) table.getCurrentPage())).orElse(0);
 
         // take care of restClient handle: maybe not useful to keep into
         AjaxDataTablePanel.Builder<T, String> resultTableBuilder = new AjaxDataTablePanel.Builder<>(
@@ -326,7 +326,7 @@ public abstract class DirectoryPanel<
     @Override
     public void onEvent(final IEvent<?> event) {
         if (event.getPayload() instanceof EventDataWrapper) {
-            final EventDataWrapper data = (EventDataWrapper) event.getPayload();
+            EventDataWrapper data = (EventDataWrapper) event.getPayload();
 
             if (data.getRows() < 1) {
                 updateResultTable(data.isCreate());
@@ -343,7 +343,7 @@ public abstract class DirectoryPanel<
 
     @Override
     protected void customActionOnFinishCallback(final AjaxRequestTarget target) {
-        final EventDataWrapper data = new EventDataWrapper();
+        EventDataWrapper data = new EventDataWrapper();
         data.setTarget(target);
         data.setRows(rows);
         send(getParent(), Broadcast.BREADTH, data);
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DynRealmModalPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DynRealmModalPanel.java
index cd3438ce93..f60b411c4d 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DynRealmModalPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DynRealmModalPanel.java
@@ -89,33 +89,33 @@ public class DynRealmModalPanel extends AbstractModalPanel<DynRealmWrapper> {
             protected void populateItem(final ListItem<AnyTypeTO> item) {
                 final String key = item.getModelObject().getKey();
                 item.add(new Accordion("dynMembershipCond", List.of(
-                    new AbstractTab(Model.of(key + " Dynamic Condition")) {
+                        new AbstractTab(Model.of(key + " Dynamic Condition")) {
 
-                        private static final long serialVersionUID = 1037272333056449378L;
+                    private static final long serialVersionUID = 1037272333056449378L;
 
-                        @Override
-                        public Panel getPanel(final String panelId) {
-                            switch (item.getModelObject().getKind()) {
-                                case USER:
-                                    return new UserSearchPanel.Builder(
-                                        new MapOfListModel<>(dynRealmWrapper, "dynClauses", key)).
+                    @Override
+                    public Panel getPanel(final String panelId) {
+                        switch (item.getModelObject().getKind()) {
+                            case USER:
+                                return new UserSearchPanel.Builder(
+                                        new MapOfListModel<>(dynRealmWrapper, "dynClauses", key), pageRef).
                                         required(false).build(panelId);
 
-                                case GROUP:
-                                    return new GroupSearchPanel.Builder(
-                                        new MapOfListModel<>(dynRealmWrapper, "dynClauses", key)).
+                            case GROUP:
+                                return new GroupSearchPanel.Builder(
+                                        new MapOfListModel<>(dynRealmWrapper, "dynClauses", key), pageRef).
                                         required(false).build(panelId);
 
-                                case ANY_OBJECT:
-                                default:
-                                    return new AnyObjectSearchPanel.Builder(
+                            case ANY_OBJECT:
+                            default:
+                                return new AnyObjectSearchPanel.Builder(
                                         key,
-                                        new MapOfListModel<>(dynRealmWrapper, "dynClauses", key)).
+                                        new MapOfListModel<>(dynRealmWrapper, "dynClauses", key), pageRef).
                                         required(false).build(panelId);
-                            }
                         }
-                    }), Model.of(StringUtils.isBlank(dynRealmWrapper.getDynMembershipConds().get(key)) ? -1 : 0)).
-                    setOutputMarkupId(true));
+                    }
+                }), Model.of(StringUtils.isBlank(dynRealmWrapper.getDynMembershipConds().get(key)) ? -1 : 0)).
+                        setOutputMarkupId(true));
             }
         });
     }
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/RealmChoicePanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/RealmChoicePanel.java
index 07d30b2ff6..6e03afccd6 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/RealmChoicePanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/RealmChoicePanel.java
@@ -28,9 +28,11 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Comparator;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import org.apache.commons.lang3.StringUtils;
@@ -160,7 +162,12 @@ public class RealmChoicePanel extends Panel {
                         placeholder.setFullPath(rootRealm);
                         return placeholder;
                     });
-        }).orElseGet(RealmTO::new);
+        }).orElseGet(() -> {
+            RealmTO root = new RealmTO();
+            root.setName(SyncopeConstants.ROOT_REALM);
+            root.setFullPath(SyncopeConstants.ROOT_REALM);
+            return root;
+        });
 
         model = Model.of(realm);
         searchQuery = realm.getName();
@@ -202,7 +209,7 @@ public class RealmChoicePanel extends Panel {
         if (SyncopeConstants.ROOT_REALM.equals(realm.getFullPath())) {
             breadcrumb.setList(List.of(realm.getFullPath()));
         } else {
-            List<String> bcitems = new ArrayList<>();
+            Set<String> bcitems = new HashSet<>();
             bcitems.add(SyncopeConstants.ROOT_REALM);
 
             String[] split = realm.getFullPath().split("/");
@@ -214,7 +221,7 @@ public class RealmChoicePanel extends Panel {
                 bcitems.add(bcitem.toString());
             }
 
-            breadcrumb.setList(bcitems);
+            breadcrumb.setList(bcitems.stream().sorted().collect(Collectors.toList()));
         }
     }
 
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/TogglePanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/TogglePanel.java
index 0f44989ce3..5f8d8aff7b 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/TogglePanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/TogglePanel.java
@@ -96,7 +96,7 @@ public abstract class TogglePanel<T extends Serializable> extends WizardMgtPanel
     public TogglePanel(final String id, final String markupId, final PageReference pageRef) {
         super(id, true);
         this.activeId = markupId;
-        final String containerID = StringUtils.isBlank(markupId) ? id : markupId;
+        String containerID = StringUtils.isBlank(markupId) ? id : markupId;
 
         setRenderBodyOnly(true);
         setOutputMarkupId(true);
@@ -149,8 +149,8 @@ public abstract class TogglePanel<T extends Serializable> extends WizardMgtPanel
     }
 
     protected void setHeader(final AjaxRequestTarget target, final String header) {
-        this.header.setDefaultModelObject(Optional.ofNullable(header).map(s -> s.length() >= 40
-                ? (s.substring(0, 30) + " ... ") : s).orElse(StringUtils.EMPTY));
+        this.header.setDefaultModelObject(Optional.ofNullable(header).
+                map(s -> StringUtils.abbreviate(s, HEADER_FIRST_ABBREVIATION)).orElse(StringUtils.EMPTY));
         target.add(this.header);
     }
 
@@ -191,7 +191,7 @@ public abstract class TogglePanel<T extends Serializable> extends WizardMgtPanel
         return key;
     }
 
-    private void updateLabelKeyValue(final Serializable modelObject) {
+    protected void updateLabelKeyValue(final Serializable modelObject) {
         header.add(new AttributeModifier(LABEL_DATA_VALUE, getTargetKey(modelObject)));
     }
 
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 3a3b7b143c..403dc8015f 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
@@ -23,18 +23,24 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
+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.SyncopeWebApplication;
 import org.apache.syncope.client.console.rest.AnyTypeClassRestClient;
 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.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.search.AbstractFiqlSearchConditionBuilder;
 import org.apache.syncope.common.lib.search.SearchableFields;
 import org.apache.syncope.common.lib.to.AnyTypeClassTO;
 import org.apache.syncope.common.lib.to.PlainSchemaTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.event.IEventSink;
 import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.panel.Panel;
@@ -74,10 +80,6 @@ public abstract class AbstractSearchPanel extends Panel {
 
     protected final String type;
 
-    protected final boolean required;
-
-    protected final boolean enableSearch;
-
     protected final GroupRestClient groupRestClient = new GroupRestClient();
 
     public abstract static class Builder<T extends AbstractSearchPanel> implements Serializable {
@@ -86,6 +88,8 @@ public abstract class AbstractSearchPanel extends Panel {
 
         protected final IModel<List<SearchClause>> model;
 
+        protected PageReference pageRef;
+
         protected boolean required = true;
 
         protected boolean enableSearch = false;
@@ -98,8 +102,9 @@ public abstract class AbstractSearchPanel extends Panel {
 
         protected IEventSink resultContainer;
 
-        public Builder(final IModel<List<SearchClause>> model) {
+        public Builder(final IModel<List<SearchClause>> model, final PageReference pageRef) {
             this.model = model;
+            this.pageRef = pageRef;
         }
 
         public Builder<T> enableSearch(final IEventSink resultContainer) {
@@ -133,13 +138,12 @@ public abstract class AbstractSearchPanel extends Panel {
             final String id, final AnyTypeKind kind, final String type, final Builder<?> builder) {
 
         super(id);
+
         populate();
 
         this.model = builder.model;
         this.typeKind = kind;
         this.type = type;
-        this.required = builder.required;
-        this.enableSearch = builder.enableSearch;
 
         setOutputMarkupId(true);
 
@@ -161,16 +165,15 @@ public abstract class AbstractSearchPanel extends Panel {
                 : Pair.of(groupNames, Model.of(0));
         SearchClausePanel searchClausePanel = new SearchClausePanel("panel", "panel",
                 Model.of(new SearchClause()),
-                required,
+                builder.required,
                 types,
                 builder.customizer,
                 anames, dnames, groupInfo, roleNames, privilegeNames, auxClassNames, resourceNames);
-
-        if (enableSearch) {
+        if (builder.enableSearch) {
             searchClausePanel.enableSearch(builder.resultContainer);
         }
 
-        MultiFieldPanel.Builder<SearchClause> searchView = new MultiFieldPanel.Builder<>(model) {
+        searchFormContainer.add(new MultiFieldPanel.Builder<>(model) {
 
             private static final long serialVersionUID = 1343431509987473047L;
 
@@ -178,9 +181,53 @@ public abstract class AbstractSearchPanel extends Panel {
             protected SearchClause newModelObject() {
                 return new SearchClause();
             }
-        };
+        }.build("search", "search", searchClausePanel).hideLabel().setOutputMarkupId(true));
+
+        FIQLQueries fiqlQueries = new FIQLQueries("fiqlQueries", this, getFIQLQueryTarget(), builder.pageRef);
+        add(fiqlQueries);
+
+        SaveFIQLQuery saveFIQLQuery = new SaveFIQLQuery("saveFIQLQuery", getFIQLQueryTarget(), builder.pageRef);
+        add(saveFIQLQuery);
+
+        ActionsPanel<Serializable> fiqlQueryActionsPanel = new ActionsPanel<>("fiqlQueryActionsPanel", null);
+        fiqlQueryActionsPanel.add(new ActionLink<>() {
+
+            private static final long serialVersionUID = 2041211756396714619L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
+                saveFIQLQuery.setFiql(
+                        SearchUtils.buildFIQL(AbstractSearchPanel.this.getModel().getObject(),
+                                getSearchConditionBuilder()).
+                                replaceAll(SearchUtils.getTypeConditionPattern(type).pattern(), ""));
+                saveFIQLQuery.toggle(target, true);
+            }
+        }, ActionLink.ActionType.EXPORT, StringUtils.EMPTY).hideLabel();
+        fiqlQueryActionsPanel.add(new ActionLink<>() {
+
+            private static final long serialVersionUID = -7978723352517770644L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
+                fiqlQueries.toggle(target, true);
+            }
+        }, ActionLink.ActionType.SELECT, StringUtils.EMPTY).hideLabel();
+        fiqlQueryActionsPanel.setVisible(
+                builder.enableSearch
+                && !model.getObject().isEmpty()
+                && !SyncopeConsoleSession.get().getSelfTO().getUsername().
+                        equals(SyncopeWebApplication.get().getAdminUser()));
+        add(fiqlQueryActionsPanel.setOutputMarkupPlaceholderTag(true));
+    }
+
+    protected abstract AbstractFiqlSearchConditionBuilder<?, ?, ?> getSearchConditionBuilder();
+
+    protected abstract String getFIQLQueryTarget();
 
-        searchFormContainer.add(searchView.build("search", "search", searchClausePanel).hideLabel());
+    protected void updateFIQL(final AjaxRequestTarget target, final String fiql) {
+        model.setObject(SearchUtils.getSearchClauses(
+                fiql.replaceAll(SearchUtils.getTypeConditionPattern(type).pattern(), "")));
+        target.add(searchFormContainer);
     }
 
     protected void populate() {
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/AnyObjectSearchPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/AnyObjectSearchPanel.java
index 282059878f..1847fddec5 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/AnyObjectSearchPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/AnyObjectSearchPanel.java
@@ -25,13 +25,16 @@ import java.util.function.Function;
 import java.util.stream.Collectors;
 import org.apache.syncope.client.console.rest.AnyTypeRestClient;
 import org.apache.syncope.client.console.rest.SchemaRestClient;
+import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.client.ui.commons.Constants;
 import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.search.AbstractFiqlSearchConditionBuilder;
 import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.common.lib.to.PlainSchemaTO;
 import org.apache.syncope.common.lib.to.SchemaTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.SchemaType;
+import org.apache.wicket.PageReference;
 import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.LoadableDetachableModel;
@@ -46,8 +49,8 @@ public class AnyObjectSearchPanel extends AbstractSearchPanel {
 
         private final String type;
 
-        public Builder(final String type, final IModel<List<SearchClause>> model) {
-            super(model);
+        public Builder(final String type, final IModel<List<SearchClause>> model, final PageReference pageRef) {
+            super(model, pageRef);
             this.type = type;
         }
 
@@ -65,6 +68,16 @@ public class AnyObjectSearchPanel extends AbstractSearchPanel {
         super(id, kind, type, builder);
     }
 
+    @Override
+    protected AbstractFiqlSearchConditionBuilder<?, ?, ?> getSearchConditionBuilder() {
+        return SyncopeClient.getAnyObjectSearchConditionBuilder(type);
+    }
+
+    @Override
+    protected String getFIQLQueryTarget() {
+        return type;
+    }
+
     @Override
     protected void populate() {
         super.populate();
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/FIQLQueries.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/FIQLQueries.java
new file mode 100644
index 0000000000..9c835970b4
--- /dev/null
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/FIQLQueries.java
@@ -0,0 +1,53 @@
+/*
+ * 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.console.panels.search;
+
+import java.io.Serializable;
+import org.apache.syncope.client.console.panels.TogglePanel;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+
+public class FIQLQueries extends TogglePanel<Serializable> {
+
+    private static final long serialVersionUID = -6210866598876608258L;
+
+    public FIQLQueries(
+            final String id,
+            final AbstractSearchPanel searchPanel,
+            final String target,
+            final PageReference pageRef) {
+
+        super(id, pageRef);
+
+        addInnerObject(new FIQLQueryDirectoryPanel("fiqlQueryDirectoryPanel", searchPanel, target, this, pageRef));
+    }
+
+    @Override
+    protected String getTargetKey(final Serializable modelObject) {
+        return getString("fiqlQueries");
+    }
+
+    @Override
+    public void toggle(final AjaxRequestTarget target, final boolean toggle) {
+        if (toggle) {
+            setHeader(target, getString("fiqlQueries"));
+        }
+        super.toggle(target, toggle);
+    }
+}
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/FIQLQueryDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/FIQLQueryDirectoryPanel.java
new file mode 100644
index 0000000000..e0c5561012
--- /dev/null
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/FIQLQueryDirectoryPanel.java
@@ -0,0 +1,176 @@
+/*
+ * 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.console.panels.search;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.commons.IdRepoConstants;
+import org.apache.syncope.client.console.commons.SortableDataProviderComparator;
+import org.apache.syncope.client.console.panels.DirectoryPanel;
+import org.apache.syncope.client.console.rest.FIQLQueryRestClient;
+import org.apache.syncope.client.console.wicket.extensions.markup.html.repeater.data.table.KeyPropertyColumn;
+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.DirectoryDataProvider;
+import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.FIQLQueryTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn;
+import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn;
+import org.apache.wicket.model.CompoundPropertyModel;
+import org.apache.wicket.model.IModel;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.StringResourceModel;
+
+public class FIQLQueryDirectoryPanel extends DirectoryPanel<
+        FIQLQueryTO, FIQLQueryTO, DirectoryDataProvider<FIQLQueryTO>, FIQLQueryRestClient> {
+
+    private static final long serialVersionUID = -913956855318099854L;
+
+    private final AbstractSearchPanel searchPanel;
+
+    private final String target;
+
+    private final FIQLQueries parent;
+
+    public FIQLQueryDirectoryPanel(
+            final String id,
+            final AbstractSearchPanel searchPanel,
+            final String target,
+            final FIQLQueries parent,
+            final PageReference pageRef) {
+
+        super(id, pageRef, false);
+        this.target = target;
+        this.searchPanel = searchPanel;
+        this.parent = parent;
+
+        disableCheckBoxes();
+
+        initResultTable();
+    }
+
+    @Override
+    protected List<IColumn<FIQLQueryTO, String>> getColumns() {
+        List<IColumn<FIQLQueryTO, String>> columns = new ArrayList<>();
+
+        columns.add(new KeyPropertyColumn<>(
+                new StringResourceModel(Constants.KEY_FIELD_NAME, this), Constants.KEY_FIELD_NAME));
+
+        columns.add(new PropertyColumn<>(new StringResourceModel(
+                Constants.NAME_FIELD_NAME, this), Constants.NAME_FIELD_NAME, Constants.NAME_FIELD_NAME));
+
+        columns.add(new PropertyColumn<>(Model.of("FIQL"), "fiql"));
+
+        return columns;
+    }
+
+    @Override
+    protected ActionsPanel<FIQLQueryTO> getActions(final IModel<FIQLQueryTO> model) {
+        ActionsPanel<FIQLQueryTO> panel = super.getActions(model);
+
+        panel.add(new ActionLink<>() {
+
+            private static final long serialVersionUID = -3722207913631435501L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final FIQLQueryTO ignore) {
+                searchPanel.updateFIQL(target, model.getObject().getFiql());
+                parent.close(target);
+            }
+        }, ActionLink.ActionType.SELECT, StringUtils.EMPTY);
+
+        panel.add(new ActionLink<>() {
+
+            private static final long serialVersionUID = -3722207913631435501L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final FIQLQueryTO ignore) {
+                try {
+                    FIQLQueryRestClient.delete(model.getObject().getKey());
+
+                    SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
+                    customActionOnFinishCallback(target);
+                } catch (SyncopeClientException e) {
+                    LOG.error("While deleting {}", model.getObject().getName(), e);
+                    SyncopeConsoleSession.get().onException(e);
+                }
+                ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
+            }
+        }, ActionLink.ActionType.DELETE, StringUtils.EMPTY);
+
+        return panel;
+    }
+
+    @Override
+    protected Collection<ActionLink.ActionType> getBatches() {
+        return Set.of();
+    }
+
+    @Override
+    protected DirectoryDataProvider<FIQLQueryTO> dataProvider() {
+        return new FIQLQueryDataProvider(rows);
+    }
+
+    @Override
+    protected String paginatorRowsKey() {
+        return IdRepoConstants.PREF_FIQL_QUERIES_PAGINATOR_ROWS;
+    }
+
+    protected class FIQLQueryDataProvider extends DirectoryDataProvider<FIQLQueryTO> {
+
+        private static final long serialVersionUID = 4725679400450513556L;
+
+        private final SortableDataProviderComparator<FIQLQueryTO> comparator;
+
+        public FIQLQueryDataProvider(final int paginatorRows) {
+            super(paginatorRows);
+
+            //Default sorting
+            setSort(Constants.NAME_FIELD_NAME, SortOrder.ASCENDING);
+            comparator = new SortableDataProviderComparator<>(this);
+        }
+
+        @Override
+        public Iterator<FIQLQueryTO> iterator(final long first, final long count) {
+            List<FIQLQueryTO> list = FIQLQueryRestClient.list(target);
+            list.sort(comparator);
+            return list.subList((int) first, (int) (first + count)).iterator();
+        }
+
+        @Override
+        public long size() {
+            return FIQLQueryRestClient.list(target).size();
+        }
+
+        @Override
+        public IModel<FIQLQueryTO> model(final FIQLQueryTO object) {
+            return new CompoundPropertyModel<>(object);
+        }
+    }
+}
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/GroupSearchPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/GroupSearchPanel.java
index a7ae709ad6..68752d515c 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/GroupSearchPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/GroupSearchPanel.java
@@ -25,10 +25,13 @@ import java.util.function.Function;
 import java.util.stream.Collectors;
 import org.apache.syncope.client.console.rest.AnyTypeRestClient;
 import org.apache.syncope.client.console.rest.SchemaRestClient;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.search.AbstractFiqlSearchConditionBuilder;
 import org.apache.syncope.common.lib.to.PlainSchemaTO;
 import org.apache.syncope.common.lib.to.SchemaTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.SchemaType;
+import org.apache.wicket.PageReference;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.LoadableDetachableModel;
 
@@ -40,8 +43,8 @@ public class GroupSearchPanel extends AbstractSearchPanel {
 
         private static final long serialVersionUID = 6308997285778809578L;
 
-        public Builder(final IModel<List<SearchClause>> model) {
-            super(model);
+        public Builder(final IModel<List<SearchClause>> model, final PageReference pageRef) {
+            super(model, pageRef);
         }
 
         @Override
@@ -54,6 +57,16 @@ public class GroupSearchPanel extends AbstractSearchPanel {
         super(id, AnyTypeKind.GROUP, builder);
     }
 
+    @Override
+    protected AbstractFiqlSearchConditionBuilder<?, ?, ?> getSearchConditionBuilder() {
+        return SyncopeClient.getGroupSearchConditionBuilder();
+    }
+
+    @Override
+    protected String getFIQLQueryTarget() {
+        return AnyTypeKind.GROUP.name();
+    }
+
     @Override
     protected void populate() {
         super.populate();
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/NewBpmnProcess.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SaveFIQLQuery.java
similarity index 55%
copy from ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/NewBpmnProcess.java
copy to client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SaveFIQLQuery.java
index 29cb1d30d8..8080a216f4 100644
--- a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/NewBpmnProcess.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SaveFIQLQuery.java
@@ -16,61 +16,63 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.client.console.panels;
+package org.apache.syncope.client.console.panels.search;
 
 import java.io.Serializable;
-import java.nio.charset.StandardCharsets;
-import javax.ws.rs.core.MediaType;
-import org.apache.commons.io.IOUtils;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
-import org.apache.syncope.client.console.rest.BpmnProcessRestClient;
+import org.apache.syncope.client.console.panels.TogglePanel;
+import org.apache.syncope.client.console.rest.FIQLQueryRestClient;
 import org.apache.syncope.client.ui.commons.Constants;
 import org.apache.syncope.client.ui.commons.pages.BaseWebPage;
+import org.apache.syncope.common.lib.to.FIQLQueryTO;
 import org.apache.wicket.PageReference;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink;
-import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.form.Form;
 import org.apache.wicket.markup.html.form.TextField;
 import org.apache.wicket.model.Model;
 
-public class NewBpmnProcess extends TogglePanel<Serializable> {
+public class SaveFIQLQuery extends TogglePanel<Serializable> {
 
-    private static final long serialVersionUID = -4886361549305302161L;
+    private static final long serialVersionUID = -1519998802447270269L;
 
-    private final Form<?> form;
+    private String fiql;
 
-    public NewBpmnProcess(final String id, final WebMarkupContainer container, final PageReference pageRef) {
+    private TextField<String> name;
+
+    public SaveFIQLQuery(final String id, final String target, final PageReference pageRef) {
         super(id, pageRef);
 
-        form = new Form<>("form");
+        Form<?> form = new Form<>("form");
         addInnerObject(form);
 
-        final TextField<String> key = new TextField<>("key", new Model<>());
-        key.setRequired(true);
-        form.add(key);
+        name = new TextField<>("name", new Model<>());
+        form.add(name);
 
         form.add(new AjaxSubmitLink("submit", form) {
 
-            private static final long serialVersionUID = 4947613489823025052L;
+            private static final long serialVersionUID = -5697330186048290602L;
 
             @Override
-            protected void onSubmit(final AjaxRequestTarget target) {
+            protected void onSubmit(final AjaxRequestTarget art) {
                 try {
-                    BpmnProcessRestClient.setDefinition(MediaType.APPLICATION_XML_TYPE, key.getModelObject(),
-                            IOUtils.toString(
-                                    NewBpmnProcess.class.getResourceAsStream("empty.bpmn20.xml"),
-                                    StandardCharsets.UTF_8).replaceAll("%KEY%", key.getModelObject()));
+                    FIQLQueryTO query = new FIQLQueryTO();
+                    query.setName(name.getModelObject());
+                    query.setTarget(target);
+                    query.setFiql(fiql);
+
+                    FIQLQueryRestClient.create(query);
+
+                    name.getModel().setObject(null);
+                    name.setRequired(false);
 
-                    key.getModel().setObject(null);
                     SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED));
-                    toggle(target, false);
-                    target.add(container);
+                    toggle(art, false);
                 } catch (Exception e) {
-                    LOG.error("While creating new BPMN process", e);
+                    LOG.error("While creating new FIQL query", e);
                     SyncopeConsoleSession.get().onException(e);
                 }
-                ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(target);
+                ((BaseWebPage) pageRef.getPage()).getNotificationPanel().refresh(art);
             }
 
             @Override
@@ -79,4 +81,17 @@ public class NewBpmnProcess extends TogglePanel<Serializable> {
             }
         });
     }
+
+    public void setFiql(final String fiql) {
+        this.fiql = fiql;
+        this.name.setRequired(true);
+    }
+
+    @Override
+    public void toggle(final AjaxRequestTarget target, final boolean toggle) {
+        if (toggle) {
+            setHeader(target, getString("newFIQLQuery"));
+        }
+        super.toggle(target, toggle);
+    }
 }
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClausePanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClausePanel.java
index e0e4e6f6d5..aa85f762d3 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClausePanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClausePanel.java
@@ -57,7 +57,6 @@ import org.apache.syncope.common.lib.to.RelationshipTypeTO;
 import org.apache.syncope.common.lib.types.AttrSchemaType;
 import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.Component;
-import org.apache.wicket.MarkupContainer;
 import org.apache.wicket.ajax.AjaxEventBehavior;
 import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.ajax.attributes.AjaxCallListener;
@@ -291,12 +290,14 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
                         return groupInfo.getLeft().getObject();
 
                     case ROLE_MEMBERSHIP:
-                        return roleNames.getObject().stream().
-                                sorted().collect(Collectors.toList());
+                        return Optional.ofNullable(roleNames).
+                                map(r -> r.getObject().stream().sorted().collect(Collectors.toList())).
+                                orElse(List.of());
 
                     case PRIVILEGE:
-                        return privilegeNames.getObject().stream().
-                                sorted().collect(Collectors.toList());
+                        return Optional.ofNullable(privilegeNames).
+                                map(p -> p.getObject().stream().sorted().collect(Collectors.toList())).
+                                orElse(List.of());
 
                     case AUX_CLASS:
                         return auxClassNames.getObject().stream().
@@ -371,11 +372,6 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
         return super.setNewModel(model);
     }
 
-    @Override
-    public final MarkupContainer add(final Component... childs) {
-        return super.add(childs);
-    }
-
     @Override
     @SuppressWarnings({ "rawtypes", "unchecked" })
     public FieldPanel<SearchClause> settingsDependingComponents() {
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchUtils.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchUtils.java
index 89efc1de18..e5be686b1c 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchUtils.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchUtils.java
@@ -65,7 +65,7 @@ public final class SearchUtils implements Serializable {
         }
     };
 
-    private static Pattern getTypeConditionPattern(final String type) {
+    public static Pattern getTypeConditionPattern(final String type) {
         return Pattern.compile(String.format(";\\$type==%s|\\$type==%s;", type, type));
     }
 
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/UserSearchPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/UserSearchPanel.java
index 47b2a0e41a..3e4e95e623 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/UserSearchPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/UserSearchPanel.java
@@ -23,8 +23,11 @@ import java.util.List;
 import java.util.stream.Collectors;
 import org.apache.syncope.client.console.rest.ApplicationRestClient;
 import org.apache.syncope.client.console.rest.RoleRestClient;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.search.AbstractFiqlSearchConditionBuilder;
 import org.apache.syncope.common.lib.to.EntityTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.wicket.PageReference;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.model.LoadableDetachableModel;
 
@@ -36,8 +39,8 @@ public class UserSearchPanel extends AnyObjectSearchPanel {
 
         private static final long serialVersionUID = 6308997285778809578L;
 
-        public Builder(final IModel<List<SearchClause>> model) {
-            super(AnyTypeKind.USER.name(), model);
+        public Builder(final IModel<List<SearchClause>> model, final PageReference pageRef) {
+            super(AnyTypeKind.USER.name(), model, pageRef);
         }
 
         @Override
@@ -50,6 +53,16 @@ public class UserSearchPanel extends AnyObjectSearchPanel {
         super(id, AnyTypeKind.USER, builder);
     }
 
+    @Override
+    protected AbstractFiqlSearchConditionBuilder<?, ?, ?> getSearchConditionBuilder() {
+        return SyncopeClient.getUserSearchConditionBuilder();
+    }
+
+    @Override
+    protected String getFIQLQueryTarget() {
+        return AnyTypeKind.USER.name();
+    }
+
     @Override
     protected void populate() {
         super.populate();
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/policies/PolicyRuleWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/policies/PolicyRuleWizardBuilder.java
index f79dc646f1..b2a38d56aa 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/policies/PolicyRuleWizardBuilder.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/policies/PolicyRuleWizardBuilder.java
@@ -171,7 +171,7 @@ public class PolicyRuleWizardBuilder extends BaseAjaxWizardBuilder<PolicyRuleWra
         }
     }
 
-    public static class Configuration extends WizardStep {
+    public class Configuration extends WizardStep {
 
         private static final long serialVersionUID = -785981096328637758L;
 
@@ -185,7 +185,7 @@ public class PolicyRuleWizardBuilder extends BaseAjaxWizardBuilder<PolicyRuleWra
                     return rule.getConf();
                 }
             };
-            add(new BeanPanel<>("bean", bean).setRenderBodyOnly(true));
+            add(new BeanPanel<>("bean", bean, pageRef).setRenderBodyOnly(true));
         }
     }
 }
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportDirectoryPanel.java
index c322182f8a..1c3375ddd2 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportDirectoryPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportDirectoryPanel.java
@@ -275,7 +275,7 @@ public abstract class ReportDirectoryPanel
 
     @Override
     protected String paginatorRowsKey() {
-        return IdRepoConstants.PREF_REPORT_TASKS_PAGINATOR_ROWS;
+        return IdRepoConstants.PREF_REPORT_PAGINATOR_ROWS;
     }
 
     protected abstract void viewReport(ReportTO reportTO, AjaxRequestTarget target);
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportletDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportletDirectoryPanel.java
index 2bfe0d04a7..9bd23ea6f4 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportletDirectoryPanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportletDirectoryPanel.java
@@ -97,7 +97,7 @@ public class ReportletDirectoryPanel extends DirectoryPanel<
 
     @Override
     protected List<IColumn<ReportletWrapper, String>> getColumns() {
-        final List<IColumn<ReportletWrapper, String>> columns = new ArrayList<>();
+        List<IColumn<ReportletWrapper, String>> columns = new ArrayList<>();
 
         columns.add(new PropertyColumn<>(
                 new StringResourceModel("reportlet", this), "implementationKey", "implementationKey"));
@@ -126,7 +126,7 @@ public class ReportletDirectoryPanel extends DirectoryPanel<
 
     @Override
     public ActionsPanel<ReportletWrapper> getActions(final IModel<ReportletWrapper> model) {
-        final ActionsPanel<ReportletWrapper> panel = super.getActions(model);
+        ActionsPanel<ReportletWrapper> panel = super.getActions(model);
 
         panel.add(new ActionLink<>() {
 
@@ -151,7 +151,7 @@ public class ReportletDirectoryPanel extends DirectoryPanel<
             public void onClick(final AjaxRequestTarget target, final ReportletWrapper ignore) {
                 final ReportletConf reportlet = model.getObject().getConf();
                 try {
-                    final ReportTO actual = ReportRestClient.read(report);
+                    ReportTO actual = ReportRestClient.read(report);
                     actual.getReportlets().remove(model.getObject().getImplementationKey());
                     ReportRestClient.update(actual);
 
@@ -198,7 +198,7 @@ public class ReportletDirectoryPanel extends DirectoryPanel<
 
     @Override
     protected String paginatorRowsKey() {
-        return IdRepoConstants.PREF_REPORTLET_TASKS_PAGINATOR_ROWS;
+        return IdRepoConstants.PREF_REPORTLET_PAGINATOR_ROWS;
     }
 
     protected class ReportDataProvider extends DirectoryDataProvider<ReportletWrapper> {
@@ -237,7 +237,7 @@ public class ReportletDirectoryPanel extends DirectoryPanel<
 
         @Override
         public Iterator<ReportletWrapper> iterator(final long first, final long count) {
-            final ReportTO actual = ReportRestClient.read(report);
+            ReportTO actual = ReportRestClient.read(report);
 
             List<ReportletWrapper> reportlets = getReportletWrappers(actual);
 
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportletWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportletWizardBuilder.java
index 601dd8ef83..ddfd301119 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportletWizardBuilder.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/reports/ReportletWizardBuilder.java
@@ -62,9 +62,8 @@ public class ReportletWizardBuilder extends BaseAjaxWizardBuilder<ReportletWrapp
     protected Serializable onApplyInternal(final ReportletWrapper modelObject) {
         if (modelObject.getImplementationEngine() == ImplementationEngine.JAVA) {
             BeanWrapper confWrapper = PropertyAccessorFactory.forBeanPropertyAccess(modelObject.getConf());
-            modelObject.getSCondWrapper().forEach((fieldName, pair) -> {
-                confWrapper.setPropertyValue(fieldName, SearchUtils.buildFIQL(pair.getRight(), pair.getLeft()));
-            });
+            modelObject.getSCondWrapper().forEach((fieldName, pair) -> confWrapper.setPropertyValue(
+                    fieldName, SearchUtils.buildFIQL(pair.getRight(), pair.getLeft())));
             ImplementationTO reportlet = ImplementationRestClient.read(
                     IdRepoImplementationType.REPORTLET, modelObject.getImplementationKey());
             try {
@@ -127,7 +126,7 @@ public class ReportletWizardBuilder extends BaseAjaxWizardBuilder<ReportletWrapp
         }
     }
 
-    public static class Configuration extends WizardStep {
+    public class Configuration extends WizardStep {
 
         private static final long serialVersionUID = -785981096328637758L;
 
@@ -141,7 +140,8 @@ public class ReportletWizardBuilder extends BaseAjaxWizardBuilder<ReportletWrapp
                     return reportlet.getConf();
                 }
             };
-            add(new BeanPanel<>("bean", bean, reportlet.getSCondWrapper(), Constants.NAME_FIELD_NAME, "reportlet").
+            add(new BeanPanel<>(
+                    "bean", bean, reportlet.getSCondWrapper(), pageRef, Constants.NAME_FIELD_NAME, "reportlet").
                     setRenderBodyOnly(true));
         }
     }
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/FIQLQueryRestClient.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/FIQLQueryRestClient.java
new file mode 100644
index 0000000000..ad45f71f72
--- /dev/null
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/FIQLQueryRestClient.java
@@ -0,0 +1,48 @@
+/*
+ * 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.console.rest;
+
+import java.util.List;
+import org.apache.syncope.common.lib.to.FIQLQueryTO;
+import org.apache.syncope.common.rest.api.service.FIQLQueryService;
+
+public class FIQLQueryRestClient extends BaseRestClient {
+
+    private static final long serialVersionUID = -3161863874876938094L;
+
+    public static void delete(final String key) {
+        getService(FIQLQueryService.class).delete(key);
+    }
+
+    public static FIQLQueryTO read(final String key) {
+        return getService(FIQLQueryService.class).read(key);
+    }
+
+    public static void update(final FIQLQueryTO roleTO) {
+        getService(FIQLQueryService.class).update(roleTO);
+    }
+
+    public static void create(final FIQLQueryTO roleTO) {
+        getService(FIQLQueryService.class).create(roleTO);
+    }
+
+    public static List<FIQLQueryTO> list(final String target) {
+        return getService(FIQLQueryService.class).list(target);
+    }
+}
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/AbstractPropagationTasks.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/AbstractPropagationTasks.java
index e59c8f2aa9..76252347f6 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/AbstractPropagationTasks.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/AbstractPropagationTasks.java
@@ -25,5 +25,4 @@ public abstract class AbstractPropagationTasks extends AbstractTasks {
     public AbstractPropagationTasks(final String id) {
         super(id);
     }
-
 }
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/AnyPropagationTasks.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/AnyPropagationTasks.java
index 9718ed466b..25f6f0d301 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/AnyPropagationTasks.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/AnyPropagationTasks.java
@@ -41,7 +41,7 @@ public class AnyPropagationTasks extends AbstractPropagationTasks {
 
         super(BaseModal.CONTENT_ID);
 
-        final MultilevelPanel mlp = new MultilevelPanel("tasks");
+        MultilevelPanel mlp = new MultilevelPanel("tasks");
         mlp.setFirstLevel(new AnyPropagationTaskDirectoryPanel(baseModal, mlp, anyTypeKind, entityKey, pageRef) {
 
             private static final long serialVersionUID = -2195387360323687302L;
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/PushTaskFilters.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/PushTaskFilters.java
index 002747dc2d..015620f904 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/PushTaskFilters.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/PushTaskFilters.java
@@ -25,6 +25,7 @@ import org.apache.syncope.client.console.panels.search.MapOfListModel;
 import org.apache.syncope.client.console.rest.AnyTypeRestClient;
 import org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion;
 import org.apache.syncope.common.lib.to.AnyTypeTO;
+import org.apache.wicket.PageReference;
 import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
 import org.apache.wicket.extensions.wizard.WizardStep;
 import org.apache.wicket.markup.html.list.ListItem;
@@ -38,7 +39,7 @@ public class PushTaskFilters extends WizardStep {
 
     private static final long serialVersionUID = 855618618337931784L;
 
-    public PushTaskFilters(final PushTaskWrapper pushTaskWrapper) {
+    public PushTaskFilters(final PushTaskWrapper pushTaskWrapper, final PageReference pageRef) {
         super();
 
         final LoadableDetachableModel<List<AnyTypeTO>> types = new LoadableDetachableModel<>() {
@@ -59,19 +60,19 @@ public class PushTaskFilters extends WizardStep {
             protected void populateItem(final ListItem<AnyTypeTO> item) {
                 final String key = item.getModelObject().getKey();
                 item.add(new Accordion("filters", List.of(
-                    new AbstractTab(new StringResourceModel(
-                        "filters", this, new Model<>(item.getModelObject()))) {
+                        new AbstractTab(new StringResourceModel(
+                                "filters", this, new Model<>(item.getModelObject()))) {
 
-                        private static final long serialVersionUID = 1037272333056449378L;
+                    private static final long serialVersionUID = 1037272333056449378L;
 
-                        @Override
-                        public Panel getPanel(final String panelId) {
-                            return new AnyObjectSearchPanel.Builder(
-                                key, new MapOfListModel<>(pushTaskWrapper, "filterClauses", key)).
+                    @Override
+                    public Panel getPanel(final String panelId) {
+                        return new AnyObjectSearchPanel.Builder(
+                                key, new MapOfListModel<>(pushTaskWrapper, "filterClauses", key), pageRef).
                                 required(false).build(panelId);
-                        }
-                    }), Model.of(StringUtils.isBlank(pushTaskWrapper.getFilters().get(key)) ? -1 : 0))
-                    .setOutputMarkupId(true));
+                    }
+                }), Model.of(StringUtils.isBlank(pushTaskWrapper.getFilters().get(key)) ? -1 : 0))
+                        .setOutputMarkupId(true));
             }
         });
     }
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java
index 7701412abc..cbf73b6c8d 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java
@@ -93,7 +93,7 @@ public class SchedTaskWizardBuilder<T extends SchedTaskTO> extends BaseAjaxWizar
         wizardModel.add(new Profile(modelObject));
         if (modelObject instanceof PushTaskTO) {
             wrapper = new PushTaskWrapper(PushTaskTO.class.cast(modelObject));
-            wizardModel.add(new PushTaskFilters(wrapper));
+            wizardModel.add(new PushTaskFilters(wrapper, pageRef));
         }
         wizardModel.add(new Schedule(modelObject));
         return wizardModel;
@@ -197,7 +197,7 @@ public class SchedTaskWizardBuilder<T extends SchedTaskTO> extends BaseAjaxWizar
 
             final AjaxSearchFieldPanel destinationRealm =
                     new AjaxSearchFieldPanel("destinationRealm", "destinationRealm",
-                        new PropertyModel<>(taskTO, "destinationRealm"), settings) {
+                            new PropertyModel<>(taskTO, "destinationRealm"), settings) {
 
                 private static final long serialVersionUID = -6390474600233486704L;
 
@@ -233,7 +233,7 @@ public class SchedTaskWizardBuilder<T extends SchedTaskTO> extends BaseAjaxWizar
             }
 
             final AjaxSearchFieldPanel sourceRealm = new AjaxSearchFieldPanel("sourceRealm", "sourceRealm",
-                new PropertyModel<>(taskTO, "sourceRealm"), settings) {
+                    new PropertyModel<>(taskTO, "sourceRealm"), settings) {
 
                 private static final long serialVersionUID = -6390474600233486704L;
 
@@ -265,7 +265,7 @@ public class SchedTaskWizardBuilder<T extends SchedTaskTO> extends BaseAjaxWizar
             AjaxPalettePanel<String> actions = new AjaxPalettePanel.Builder<String>().
                     setAllowMoveAll(true).setAllowOrder(true).
                     build("actions",
-                        new PropertyModel<>(taskTO, "actions"),
+                            new PropertyModel<>(taskTO, "actions"),
                             new ListModel<>(taskTO instanceof PushTaskTO
                                     ? pushActions.getObject() : pullActions.getObject()));
             actions.setOutputMarkupId(true);
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksTogglePanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksTogglePanel.java
index a5eefe348f..b22eb5c37b 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksTogglePanel.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLinksTogglePanel.java
@@ -119,7 +119,7 @@ public class ActionLinksTogglePanel<T extends Serializable> extends TogglePanel<
             header = new ResourceModel("actions", StringUtils.EMPTY).getObject();
         }
 
-        setHeader(target, StringUtils.abbreviate(header, HEADER_FIRST_ABBREVIATION));
+        setHeader(target, header);
     }
 
     public void toggleWithContent(
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/UserSelectionWizardStep.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/UserSelectionWizardStep.java
index c33dd8e37e..13f85e3f24 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/UserSelectionWizardStep.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/UserSelectionWizardStep.java
@@ -58,7 +58,7 @@ public class UserSelectionWizardStep extends WizardStep {
         setTitleModel(title);
 
         userSearchPanel = UserSearchPanel.class.cast(new UserSearchPanel.Builder(
-                new ListModel<>(new ArrayList<>())).required(false).enableSearch(UserSelectionWizardStep.this).
+                new ListModel<>(new ArrayList<>()), pageRef).required(false).enableSearch(UserSelectionWizardStep.this).
                 build("usersearch"));
         add(userSearchPanel);
 
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyWizardBuilder.java
index 02d9c14f9f..a394edf827 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyWizardBuilder.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/AnyWizardBuilder.java
@@ -102,7 +102,7 @@ public abstract class AnyWizardBuilder<A extends AnyTO> extends AbstractAnyWizar
                 wizardModel.add(new Ownership(GroupWrapper.class.cast(modelObject), pageRef));
             }
             if (groupFormLayoutInfo.isDynamicMemberships()) {
-                wizardModel.add(new DynamicMemberships(GroupWrapper.class.cast(modelObject)));
+                wizardModel.add(new DynamicMemberships(GroupWrapper.class.cast(modelObject), pageRef));
             }
         }
 
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/DynamicMemberships.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/DynamicMemberships.java
index d60116b400..d84eba0b99 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/DynamicMemberships.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/DynamicMemberships.java
@@ -28,6 +28,7 @@ import org.apache.syncope.client.console.rest.AnyTypeRestClient;
 import org.apache.syncope.client.ui.commons.wicket.markup.html.bootstrap.tabs.Accordion;
 import org.apache.syncope.common.lib.to.AnyTypeTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.wicket.PageReference;
 import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
 import org.apache.wicket.extensions.wizard.WizardStep;
 import org.apache.wicket.markup.html.list.ListItem;
@@ -43,7 +44,7 @@ public class DynamicMemberships extends WizardStep {
 
     private static final long serialVersionUID = 855618618337931784L;
 
-    public DynamicMemberships(final GroupWrapper groupWrapper) {
+    public DynamicMemberships(final GroupWrapper groupWrapper, final PageReference pageRef) {
         super();
 
         final LoadableDetachableModel<List<AnyTypeTO>> types = new LoadableDetachableModel<>() {
@@ -68,7 +69,7 @@ public class DynamicMemberships extends WizardStep {
 
             @Override
             public Panel getPanel(final String panelId) {
-                return new UserSearchPanel.Builder(new PropertyModel<>(groupWrapper, "uDynClauses")).
+                return new UserSearchPanel.Builder(new PropertyModel<>(groupWrapper, "uDynClauses"), pageRef).
                         required(true).build(panelId);
             }
         }), Model.of(StringUtils.isBlank(groupWrapper.getUDynMembershipCond()) ? -1 : 0)).setOutputMarkupId(true));
@@ -93,7 +94,7 @@ public class DynamicMemberships extends WizardStep {
                         @Override
                         public Panel getPanel(final String panelId) {
                             return new AnyObjectSearchPanel.Builder(
-                                key, new MapOfListModel<>(groupWrapper, "aDynClauses", key)).
+                                key, new MapOfListModel<>(groupWrapper, "aDynClauses", key), pageRef).
                                 required(false).build(panelId);
                         }
                     }), Model.of(StringUtils.isBlank(groupWrapper.getADynMembershipConds().get(key)) ? -1 : 0)).
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Ownership.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Ownership.java
index a1130cbbdc..b1fd4fa604 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Ownership.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Ownership.java
@@ -99,7 +99,7 @@ public class Ownership extends WizardStep implements ICondition {
         // -----------------------------------------------------------------
         // Pre-Authorizations
         // -----------------------------------------------------------------
-        final ActionPermissions permissions = new ActionPermissions();
+        ActionPermissions permissions = new ActionPermissions();
         setMetaData(MetaDataRoleAuthorizationStrategy.ACTION_PERMISSIONS, permissions);
         permissions.authorize(RENDER, new Roles(IdRepoEntitlement.USER_SEARCH));
         // -----------------------------------------------------------------
@@ -109,7 +109,7 @@ public class Ownership extends WizardStep implements ICondition {
 
         isGroupOwnership = Model.of(groupWrapper.getInnerObject().getGroupOwner() != null);
 
-        final BootstrapToggleConfig config = new BootstrapToggleConfig().
+        BootstrapToggleConfig config = new BootstrapToggleConfig().
                 withOnStyle(BootstrapToggleConfig.Style.info).
                 withOffStyle(BootstrapToggleConfig.Style.warning).
                 withSize(BootstrapToggleConfig.Size.mini);
@@ -166,32 +166,27 @@ public class Ownership extends WizardStep implements ICondition {
 
         groupSearchFragment = new Fragment("search", "groupSearchFragment", this);
         groupSearchPanel = new GroupSearchPanel.Builder(
-                new ListModel<>(new ArrayList<>())).required(false).enableSearch(Ownership.this).
+                new ListModel<>(new ArrayList<>()), pageRef).required(false).enableSearch(Ownership.this).
                 build("groupsearch");
         groupSearchFragment.add(groupSearchPanel.setRenderBodyOnly(true));
 
         AnyTypeTO anyTypeTO = AnyTypeRestClient.read(AnyTypeKind.GROUP.name());
-
         groupDirectoryPanel = GroupSelectionDirectoryPanel.class.cast(new GroupSelectionDirectoryPanel.Builder(
                 AnyTypeClassRestClient.list(anyTypeTO.getClasses()),
                 anyTypeTO.getKey(),
                 pageRef).build("searchResult"));
-
         groupSearchFragment.add(groupDirectoryPanel);
 
         userSearchFragment = new Fragment("search", "userSearchFragment", this);
         userSearchPanel = UserSearchPanel.class.cast(new UserSearchPanel.Builder(
-                new ListModel<>(new ArrayList<>())).required(false).enableSearch(Ownership.this).
+                new ListModel<>(new ArrayList<>()), pageRef).required(false).enableSearch(Ownership.this).
                 build("usersearch"));
         userSearchFragment.add(userSearchPanel.setRenderBodyOnly(true));
 
         anyTypeTO = AnyTypeRestClient.read(AnyTypeKind.USER.name());
-
         userDirectoryPanel = UserSelectionDirectoryPanel.class.cast(new UserSelectionDirectoryPanel.Builder(
-                AnyTypeClassRestClient.list(anyTypeTO.getClasses()),
-                anyTypeTO.getKey(),
-                pageRef).build("searchResult"));
-
+                AnyTypeClassRestClient.list(anyTypeTO.getClasses()), anyTypeTO.getKey(), pageRef).
+                build("searchResult"));
         userSearchFragment.add(userDirectoryPanel);
 
         if (isGroupOwnership.getObject()) {
@@ -200,7 +195,7 @@ public class Ownership extends WizardStep implements ICondition {
             ownerContainer.add(userSearchFragment);
         }
 
-        final AjaxTextFieldPanel userOwner = new AjaxTextFieldPanel(
+        AjaxTextFieldPanel userOwner = new AjaxTextFieldPanel(
                 "userOwner", "userOwner", new PropertyModel<>(groupWrapper.getInnerObject(), "userOwner") {
 
             private static final long serialVersionUID = -3743432456095828573L;
@@ -209,14 +204,14 @@ public class Ownership extends WizardStep implements ICondition {
             public String getObject() {
                 if (groupWrapper.getInnerObject().getUserOwner() == null) {
                     return StringUtils.EMPTY;
-                } else {
-                    UserTO userTO = userRestClient.read(groupWrapper.getInnerObject().getUserOwner());
-                    if (userTO == null) {
-                        return StringUtils.EMPTY;
-                    } else {
-                        return String.format("[%s] %s", userTO.getKey(), userTO.getUsername());
-                    }
                 }
+
+                UserTO userTO = userRestClient.read(groupWrapper.getInnerObject().getUserOwner());
+                if (userTO == null) {
+                    return StringUtils.EMPTY;
+                }
+
+                return String.format("[%s] %s", userTO.getKey(), userTO.getUsername());
             }
 
             @Override
@@ -224,7 +219,7 @@ public class Ownership extends WizardStep implements ICondition {
                 if (StringUtils.isBlank(object)) {
                     groupWrapper.getInnerObject().setUserOwner(null);
                 } else {
-                    final Matcher matcher = owner.matcher(object);
+                    Matcher matcher = owner.matcher(object);
                     if (matcher.matches()) {
                         groupWrapper.getInnerObject().setUserOwner(matcher.group(1));
                     }
@@ -236,7 +231,7 @@ public class Ownership extends WizardStep implements ICondition {
         userOwner.setReadOnly(true).setOutputMarkupId(true);
         userSearchFragment.add(userOwner);
 
-        final IndicatingAjaxLink<Void> userOwnerReset = new IndicatingAjaxLink<>("userOwnerReset") {
+        IndicatingAjaxLink<Void> userOwnerReset = new IndicatingAjaxLink<>("userOwnerReset") {
 
             private static final long serialVersionUID = -7978723352517770644L;
 
@@ -253,7 +248,7 @@ public class Ownership extends WizardStep implements ICondition {
         };
         userSearchFragment.add(userOwnerReset);
 
-        final AjaxTextFieldPanel groupOwner = new AjaxTextFieldPanel(
+        AjaxTextFieldPanel groupOwner = new AjaxTextFieldPanel(
                 "groupOwner", "groupOwner", new PropertyModel<>(groupWrapper.getInnerObject(), "groupOwner") {
 
             private static final long serialVersionUID = -3743432456095828573L;
@@ -289,7 +284,7 @@ public class Ownership extends WizardStep implements ICondition {
         groupOwner.setReadOnly(true).setOutputMarkupId(true);
         groupSearchFragment.add(groupOwner);
 
-        final IndicatingAjaxLink<Void> groupOwnerReset = new IndicatingAjaxLink<>("groupOwnerReset") {
+        IndicatingAjaxLink<Void> groupOwnerReset = new IndicatingAjaxLink<>("groupOwnerReset") {
 
             private static final long serialVersionUID = -7978723352517770644L;
 
@@ -310,18 +305,18 @@ public class Ownership extends WizardStep implements ICondition {
     @Override
     public void onEvent(final IEvent<?> event) {
         if (event.getPayload() instanceof SearchClausePanel.SearchEvent) {
-            final AjaxRequestTarget target = SearchClausePanel.SearchEvent.class.cast(event.getPayload()).getTarget();
+            AjaxRequestTarget target = SearchClausePanel.SearchEvent.class.cast(event.getPayload()).getTarget();
             if (Ownership.this.isGroupOwnership.getObject()) {
-                final String fiql = SearchUtils.buildFIQL(
+                String fiql = SearchUtils.buildFIQL(
                         groupSearchPanel.getModel().getObject(), SyncopeClient.getGroupSearchConditionBuilder());
                 groupDirectoryPanel.search(fiql, target);
             } else {
-                final String fiql = SearchUtils.buildFIQL(
+                String fiql = SearchUtils.buildFIQL(
                         userSearchPanel.getModel().getObject(), SyncopeClient.getUserSearchConditionBuilder());
                 userDirectoryPanel.search(fiql, target);
             }
         } else if (event.getPayload() instanceof AnySelectionDirectoryPanel.ItemSelection) {
-            final AnyTO sel = ((AnySelectionDirectoryPanel.ItemSelection) event.getPayload()).getSelection();
+            AnyTO sel = ((AnySelectionDirectoryPanel.ItemSelection) event.getPayload()).getSelection();
             if (sel == null) {
                 wrapper.getInnerObject().setUserOwner(null);
                 wrapper.getInnerObject().setGroupOwner(null);
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Relationships.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Relationships.java
index 61a6b385a7..1c29c5c9df 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Relationships.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Relationships.java
@@ -351,7 +351,8 @@ public class Relationships extends WizardStep implements ICondition {
         protected void setupFragment(final AnyTypeTO anyType) {
             anyObjectSearchPanel = new AnyObjectSearchPanel.Builder(
                     anyType.getKey(),
-                    new ListModel<>(new ArrayList<>())).
+                    new ListModel<>(new ArrayList<>()),
+                    pageRef).
                     enableSearch(Specification.this).
                     build("searchPanel");
             fragment.addOrReplace(anyObjectSearchPanel.setRenderBodyOnly(true));
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 c80f5f0f5f..56d6a85457 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
@@ -98,7 +98,7 @@ public class RoleWizardBuilder extends BaseAjaxWizardBuilder<RoleWrapper> {
         return wizardModel;
     }
 
-    public static class Details extends WizardStep {
+    public class Details extends WizardStep {
 
         private static final long serialVersionUID = 5514523040031722255L;
 
@@ -120,7 +120,7 @@ public class RoleWizardBuilder extends BaseAjaxWizardBuilder<RoleWrapper> {
                 @Override
                 public Panel getPanel(final String panelId) {
                     return new UserSearchPanel.Builder(
-                            new PropertyModel<>(modelObject, "dynClauses")).
+                            new PropertyModel<>(modelObject, "dynClauses"), pageRef).
                             required(true).build(panelId);
                 }
             }), Model.of(StringUtils.isBlank(modelObject.getDynMembershipCond()) ? -1 : 0)).setOutputMarkupId(true));
@@ -135,21 +135,21 @@ public class RoleWizardBuilder extends BaseAjaxWizardBuilder<RoleWrapper> {
         public Entitlements(final RoleTO modelObject) {
             setTitleModel(new ResourceModel("entitlements"));
             add(new AjaxPalettePanel.Builder<String>().build("entitlements",
-                new PropertyModel<>(modelObject, "entitlements") {
+                    new PropertyModel<>(modelObject, "entitlements") {
 
-                    private static final long serialVersionUID = -7809699384012595307L;
+                private static final long serialVersionUID = -7809699384012595307L;
 
-                    @Override
-                    public List<String> getObject() {
-                        return new ArrayList<>(modelObject.getEntitlements());
-                    }
+                @Override
+                public List<String> getObject() {
+                    return new ArrayList<>(modelObject.getEntitlements());
+                }
 
-                    @Override
-                    public void setObject(final List<String> object) {
-                        modelObject.getEntitlements().clear();
-                        modelObject.getEntitlements().addAll(object);
-                    }
-                }, new ListModel<>(RoleRestClient.getAllAvailableEntitlements())).
+                @Override
+                public void setObject(final List<String> object) {
+                    modelObject.getEntitlements().clear();
+                    modelObject.getEntitlements().addAll(object);
+                }
+            }, new ListModel<>(RoleRestClient.getAllAvailableEntitlements())).
                     hideLabel().setOutputMarkupId(true));
         }
     }
diff --git a/client/idrepo/console/src/main/resources/console.properties b/client/idrepo/console/src/main/resources/console.properties
index 489bdd6355..33f4ac2f2f 100644
--- a/client/idrepo/console/src/main/resources/console.properties
+++ b/client/idrepo/console/src/main/resources/console.properties
@@ -46,6 +46,8 @@ console.maxUploadFileSizeMB=5
 # Max wait time on apply changes from modals/wizards (given in seconds)
 console.maxWaitTimeOnApplyChanges=30
 
+console.adminUser=${adminUser}
+
 console.reconciliationReportKey=c3520ad9-179f-49e7-b315-d684d216dd97
 
 console.page.dashboard=org.apache.syncope.client.console.pages.Dashboard
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/AbstractSearchPanel.html b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/AbstractSearchPanel.html
index 36be85b26d..dc56fb61e5 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/AbstractSearchPanel.html
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/AbstractSearchPanel.html
@@ -22,6 +22,11 @@ under the License.
   </wicket:head>
   <wicket:panel>
     <span class="searchBox">
+      <div class="col-xs-12" id="tablehandling">
+        <span wicket:id="saveFIQLQuery"/>
+        <span wicket:id="fiqlQueries"/>
+        <span wicket:id="fiqlQueryActionsPanel"/>
+      </div>
       <div class="col-xs-12">
         <span wicket:id="searchFormContainer">
           <span wicket:id="search">[SEARCH]</span>
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/AbstractSearchPanel.html b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/FIQLQueries.html
similarity index 70%
copy from client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/AbstractSearchPanel.html
copy to client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/FIQLQueries.html
index 36be85b26d..2c0506883e 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/AbstractSearchPanel.html
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/FIQLQueries.html
@@ -17,16 +17,9 @@ specific language governing permissions and limitations
 under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
-  <wicket:head>
-    <link rel="stylesheet" type="text/css" href="ui-commons/css/search.css" media="all"/>
-  </wicket:head>
-  <wicket:panel>
-    <span class="searchBox">
-      <div class="col-xs-12">
-        <span wicket:id="searchFormContainer">
-          <span wicket:id="search">[SEARCH]</span>
-        </span>
-      </div>
-    </span>
-  </wicket:panel>
+  <wicket:extend>
+    <div id="startAtContainer">
+      <span wicket:id="fiqlQueryDirectoryPanel"/>
+    </div>
+  </wicket:extend>
 </html>
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/FIQLQueries.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/FIQLQueries.properties
new file mode 100644
index 0000000000..d79efd2c19
--- /dev/null
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/FIQLQueries.properties
@@ -0,0 +1,17 @@
+# 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.
+fiqlQueries=FIQL Queries
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/FIQLQueries_fr_CA.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/FIQLQueries_fr_CA.properties
new file mode 100644
index 0000000000..d79efd2c19
--- /dev/null
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/FIQLQueries_fr_CA.properties
@@ -0,0 +1,17 @@
+# 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.
+fiqlQueries=FIQL Queries
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/FIQLQueries_it.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/FIQLQueries_it.properties
new file mode 100644
index 0000000000..e2272f8323
--- /dev/null
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/FIQLQueries_it.properties
@@ -0,0 +1,18 @@
+# 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.
+fiqlQueries=Query FIQL
+
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/FIQLQueries_ja.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/FIQLQueries_ja.properties
new file mode 100644
index 0000000000..d79efd2c19
--- /dev/null
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/FIQLQueries_ja.properties
@@ -0,0 +1,17 @@
+# 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.
+fiqlQueries=FIQL Queries
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/FIQLQueries_pt_BR.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/FIQLQueries_pt_BR.properties
new file mode 100644
index 0000000000..1908727d1a
--- /dev/null
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/FIQLQueries_pt_BR.properties
@@ -0,0 +1,18 @@
+# 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.
+fiqlQueries=FIQL Queries
+
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/FIQLQueries_ru.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/FIQLQueries_ru.properties
new file mode 100644
index 0000000000..1908727d1a
--- /dev/null
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/FIQLQueries_ru.properties
@@ -0,0 +1,18 @@
+# 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.
+fiqlQueries=FIQL Queries
+
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/AbstractSearchPanel.html b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SaveFIQLQuery.html
similarity index 69%
copy from client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/AbstractSearchPanel.html
copy to client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SaveFIQLQuery.html
index 36be85b26d..c855844ecb 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/AbstractSearchPanel.html
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SaveFIQLQuery.html
@@ -17,16 +17,16 @@ specific language governing permissions and limitations
 under the License.
 -->
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org">
-  <wicket:head>
-    <link rel="stylesheet" type="text/css" href="ui-commons/css/search.css" media="all"/>
-  </wicket:head>
-  <wicket:panel>
-    <span class="searchBox">
-      <div class="col-xs-12">
-        <span wicket:id="searchFormContainer">
-          <span wicket:id="search">[SEARCH]</span>
-        </span>
-      </div>
-    </span>
-  </wicket:panel>
+  <wicket:extend>
+    <div id="startAtContainer">
+      <form wicket:id="form">
+        <div class="input-group">
+          <input wicket:id="name"/>
+          <div class="input-group-append input-group-text">
+            <a wicket:id="submit"><i class="fas fa-archive" alt="new" title="new"></i></a>
+          </div>
+        </div>
+      </form>
+    </div>
+  </wicket:extend>
 </html>
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SaveFIQLQuery.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SaveFIQLQuery.properties
new file mode 100644
index 0000000000..3dd424d666
--- /dev/null
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SaveFIQLQuery.properties
@@ -0,0 +1,17 @@
+# 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.
+newFIQLQuery=New FIQL Query
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SaveFIQLQuery_fr_CA.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SaveFIQLQuery_fr_CA.properties
new file mode 100644
index 0000000000..3dd424d666
--- /dev/null
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SaveFIQLQuery_fr_CA.properties
@@ -0,0 +1,17 @@
+# 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.
+newFIQLQuery=New FIQL Query
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SaveFIQLQuery_it.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SaveFIQLQuery_it.properties
new file mode 100644
index 0000000000..1d6c4a17fb
--- /dev/null
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SaveFIQLQuery_it.properties
@@ -0,0 +1,18 @@
+# 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.
+newFIQLQuery=Nuova Query FIQL
+
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SaveFIQLQuery_ja.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SaveFIQLQuery_ja.properties
new file mode 100644
index 0000000000..3dd424d666
--- /dev/null
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SaveFIQLQuery_ja.properties
@@ -0,0 +1,17 @@
+# 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.
+newFIQLQuery=New FIQL Query
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SaveFIQLQuery_pt_BR.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SaveFIQLQuery_pt_BR.properties
new file mode 100644
index 0000000000..d87e548380
--- /dev/null
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SaveFIQLQuery_pt_BR.properties
@@ -0,0 +1,18 @@
+# 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.
+newFIQLQuery=New FIQL Query
+
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SaveFIQLQuery_ru.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SaveFIQLQuery_ru.properties
new file mode 100644
index 0000000000..d87e548380
--- /dev/null
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SaveFIQLQuery_ru.properties
@@ -0,0 +1,18 @@
+# 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.
+newFIQLQuery=New FIQL Query
+
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SearchClausePanel.html b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SearchClausePanel.html
index 0fe094a2ed..56d11ac473 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SearchClausePanel.html
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/search/SearchClausePanel.html
@@ -22,7 +22,7 @@ under the License.
       <label wicket:id="field-label">[LABEL]</label><span wicket:id="required"/>
       <span wicket:id="externalAction"/>
     </wicket:enclosure>
-    <span wicket:id="container"  class="clause">
+    <span wicket:id="container" class="clause">
       <div wicket:id="operatorContainer" class="field operator"><span wicket:id="operator"/></div>
       <span wicket:id="type" class="field type"/>
       <span wicket:id="property" class="field property"/>
diff --git a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/AbstractTest.java b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/AbstractTest.java
index b21e205d88..4b7d50caa0 100644
--- a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/AbstractTest.java
+++ b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/AbstractTest.java
@@ -72,6 +72,7 @@ import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.AttrSchemaType;
 import org.apache.syncope.common.rest.api.beans.SchemaQuery;
 import org.apache.syncope.common.rest.api.service.AnyTypeService;
+import org.apache.syncope.common.rest.api.service.FIQLQueryService;
 import org.apache.syncope.common.rest.api.service.SchemaService;
 import org.apache.syncope.common.rest.api.service.SyncopeService;
 import org.apache.wicket.util.tester.WicketTester;
@@ -211,6 +212,9 @@ public abstract class AbstractTest {
         public interface SchemaServiceClient extends SchemaService, Client {
         }
 
+        public interface FIQLQueryServiceClient extends FIQLQueryService, Client {
+        }
+
         private SyncopeService getSyncopeService() {
             SyncopeServiceClient service = mock(SyncopeServiceClient.class);
             when(service.type(anyString())).thenReturn(service);
@@ -248,6 +252,15 @@ public abstract class AbstractTest {
             return service;
         }
 
+        private FIQLQueryService getFIQLQueryService() {
+            FIQLQueryServiceClient service = mock(FIQLQueryServiceClient.class);
+
+            when(service.type(anyString())).thenReturn(service);
+            when(service.accept(anyString())).thenReturn(service);
+
+            return service;
+        }
+
         private UserTO getUserTO() {
             UserTO userTO = new UserTO();
             userTO.setUsername("username");
@@ -298,6 +311,9 @@ public abstract class AbstractTest {
             AnyTypeService anyTypeService = getAnyTypeService();
             when(client.getService(AnyTypeService.class)).thenReturn(anyTypeService);
 
+            FIQLQueryService fiqlQueryService = getFIQLQueryService();
+            when(client.getService(FIQLQueryService.class)).thenReturn(fiqlQueryService);
+
             SyncopeClientFactoryBean clientFactory = mock(SyncopeClientFactoryBean.class);
             when(clientFactory.setDomain(any())).thenReturn(clientFactory);
             when(clientFactory.create(any(AuthenticationHandler.class))).thenReturn(client);
diff --git a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/panels/search/UserSearchPanelTest.java b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/panels/search/UserSearchPanelTest.java
index 3c382f4974..f6e26c379d 100644
--- a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/panels/search/UserSearchPanelTest.java
+++ b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/panels/search/UserSearchPanelTest.java
@@ -20,11 +20,15 @@ package org.apache.syncope.client.console.panels.search;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import java.util.List;
 import org.apache.syncope.client.console.AbstractAdminTest;
+import org.apache.syncope.client.console.pages.BasePage;
 import org.apache.syncope.client.ui.commons.Constants;
 import org.apache.wicket.Component;
+import org.apache.wicket.PageReference;
 import org.apache.wicket.model.util.ListModel;
 import org.apache.wicket.util.tester.FormTester;
 import org.junit.jupiter.api.Test;
@@ -33,13 +37,18 @@ public class UserSearchPanelTest extends AbstractAdminTest {
 
     @Test
     public void test() {
+        BasePage page = new BasePage();
+        PageReference pageRef = mock(PageReference.class);
+        when(pageRef.getPage()).thenReturn(page);
+
         SearchClause clause = new SearchClause();
         clause.setComparator(SearchClause.Comparator.EQUALS);
         clause.setType(SearchClause.Type.ATTRIBUTE);
         clause.setProperty("username");
 
         TESTER.startComponentInPage(new UserSearchPanel.Builder(
-                new ListModel<>(List.of(clause))).required(true).enableSearch().build("content"));
+                new ListModel<>(List.of(clause)), pageRef).
+                required(true).enableSearch().build("content"));
 
         FormTester formTester = TESTER.newFormTester(
                 "content:searchFormContainer:search:multiValueContainer:innerForm");
diff --git a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/EnduserProperties.java b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/EnduserProperties.java
index 95fd53f1da..e2d75919a3 100644
--- a/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/EnduserProperties.java
+++ b/client/idrepo/enduser/src/main/java/org/apache/syncope/client/enduser/EnduserProperties.java
@@ -28,8 +28,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
 @ConfigurationProperties("enduser")
 public class EnduserProperties extends CommonUIProperties {
 
-    private String adminUser;
-
     private Class<? extends Sidebar> sidebar = Sidebar.class;
 
     private String customFormLayout = "classpath:/customFormLayout.json";
@@ -38,14 +36,6 @@ public class EnduserProperties extends CommonUIProperties {
 
     private final Map<String, Class<? extends BasePage>> page = new HashMap<>();
 
-    public String getAdminUser() {
-        return adminUser;
-    }
-
-    public void setAdminUser(final String adminUser) {
-        this.adminUser = adminUser;
-    }
-
     public Class<? extends Sidebar> getSidebar() {
         return sidebar;
     }
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/FIQLQueryTO.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/FIQLQueryTO.java
new file mode 100644
index 0000000000..beafb2d83e
--- /dev/null
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/FIQLQueryTO.java
@@ -0,0 +1,107 @@
+/*
+ * 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.common.lib.to;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import javax.ws.rs.PathParam;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+
+public class FIQLQueryTO implements NamedEntityTO {
+
+    private static final long serialVersionUID = -4467481248062334069L;
+
+    private String key;
+
+    private String name;
+
+    private String target;
+
+    private String fiql;
+
+    @Schema(accessMode = Schema.AccessMode.READ_ONLY)
+    @Override
+    public String getKey() {
+        return key;
+    }
+
+    @PathParam("key")
+    @Override
+    public void setKey(final String key) {
+        this.key = key;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    public String getTarget() {
+        return target;
+    }
+
+    public void setTarget(final String target) {
+        this.target = target;
+    }
+
+    public String getFiql() {
+        return fiql;
+    }
+
+    public void setFiql(final String fiql) {
+        this.fiql = fiql;
+    }
+
+    @Override
+    public int hashCode() {
+        return new HashCodeBuilder().
+                appendSuper(super.hashCode()).
+                append(key).
+                append(name).
+                append(target).
+                append(fiql).
+                build();
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final FIQLQueryTO other = (FIQLQueryTO) obj;
+        return new EqualsBuilder().
+                appendSuper(super.equals(obj)).
+                append(key, other.key).
+                append(name, other.name).
+                append(target, other.target).
+                append(fiql, other.fiql).
+                build();
+    }
+}
diff --git a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/FIQLQueryService.java b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/FIQLQueryService.java
new file mode 100644
index 0000000000..83e2a3c456
--- /dev/null
+++ b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/FIQLQueryService.java
@@ -0,0 +1,124 @@
+/*
+ * 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.common.rest.api.service;
+
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import io.swagger.v3.oas.annotations.headers.Header;
+import io.swagger.v3.oas.annotations.media.Schema;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import io.swagger.v3.oas.annotations.security.SecurityRequirement;
+import io.swagger.v3.oas.annotations.security.SecurityRequirements;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import java.util.List;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.to.FIQLQueryTO;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+
+/**
+ * REST operations for FIQL queries.
+ */
+@Tag(name = "FIQLQueries")
+@SecurityRequirements({
+    @SecurityRequirement(name = "BasicAuthentication"),
+    @SecurityRequirement(name = "Bearer") })
+@Path("fiqlQueries")
+public interface FIQLQueryService extends JAXRSService {
+
+    /**
+     * Returns a list of all FIQL queries for the calling user, matching the given target if provided.
+     *
+     * @param target FIQL query target
+     * @return list of all FIQL queries for the calling user, matching the given target if provided
+     */
+    @GET
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    List<FIQLQueryTO> list(@QueryParam("target") String target);
+
+    /**
+     * Returns FIQL querywith matching key.
+     *
+     * @param key FIQL querykey to be read
+     * @return FIQL querywith matching key
+     */
+    @GET
+    @Path("{key}")
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    FIQLQueryTO read(@NotNull @PathParam("key") String key);
+
+    /**
+     * Creates a new fiqlQuery.
+     *
+     * @param fiqlQueryTO FIQL queryto be created
+     * @return Response object featuring Location header of created fiqlQuery
+     */
+    @ApiResponses(
+            @ApiResponse(responseCode = "201",
+                    description = "FIQLQuery successfully created", headers = {
+                @Header(name = RESTHeaders.RESOURCE_KEY, schema =
+                        @Schema(type = "string"),
+                        description = "Key value for the entity created"),
+                @Header(name = HttpHeaders.LOCATION, schema =
+                        @Schema(type = "string"),
+                        description = "URL of the entity created") }))
+    @POST
+    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    Response create(@NotNull FIQLQueryTO fiqlQueryTO);
+
+    /**
+     * Updates the FIQL querymatching the provided key.
+     *
+     * @param fiqlQueryTO FIQL queryto be stored
+     */
+    @Parameter(name = "key", description = "FIQLQuery's key", in = ParameterIn.PATH, schema =
+            @Schema(type = "string"))
+    @ApiResponses(
+            @ApiResponse(responseCode = "204", description = "Operation was successful"))
+    @PUT
+    @Path("{key}")
+    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    void update(@NotNull FIQLQueryTO fiqlQueryTO);
+
+    /**
+     * Deletes the FIQL querymatching the provided key.
+     *
+     * @param key FIQL querykey to be deleted
+     */
+    @ApiResponses(
+            @ApiResponse(responseCode = "204", description = "Operation was successful"))
+    @DELETE
+    @Path("{key}")
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    void delete(@NotNull @PathParam("key") String key);
+}
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/DelegationLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/DelegationLogic.java
index f53e85ca09..a9f0c967e8 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/DelegationLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/DelegationLogic.java
@@ -127,7 +127,6 @@ public class DelegationLogic extends AbstractTransactionalLogic<DelegationTO> {
         Delegation delegation = delegationDAO.find(key);
         if (delegation == null) {
             LOG.error("Could not find delegation '" + key + "'");
-
             throw new NotFoundException(key);
         }
 
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/FIQLQueryLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/FIQLQueryLogic.java
new file mode 100644
index 0000000000..0ec7da4e6b
--- /dev/null
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/FIQLQueryLogic.java
@@ -0,0 +1,143 @@
+/*
+ * 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.core.logic;
+
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.to.FIQLQueryTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.dao.FIQLQueryDAO;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.FIQLQuery;
+import org.apache.syncope.core.provisioning.api.data.FIQLQueryDataBinder;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.apache.syncope.core.spring.security.DelegatedAdministrationException;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.transaction.annotation.Transactional;
+
+public class FIQLQueryLogic extends AbstractTransactionalLogic<FIQLQueryTO> {
+
+    protected final FIQLQueryDataBinder binder;
+
+    protected final FIQLQueryDAO fiqlQueryDAO;
+
+    protected final UserDAO userDAO;
+
+    public FIQLQueryLogic(
+            final FIQLQueryDataBinder binder,
+            final FIQLQueryDAO fiqlQueryDAO,
+            final UserDAO userDAO) {
+
+        this.binder = binder;
+        this.fiqlQueryDAO = fiqlQueryDAO;
+        this.userDAO = userDAO;
+    }
+
+    protected void securityChecks(final String owner) {
+        if (!AuthContextUtils.getUsername().equals(owner)) {
+            throw new DelegatedAdministrationException(SyncopeConstants.ROOT_REALM, AnyTypeKind.USER.name(), owner);
+        }
+    }
+
+    @PreAuthorize("isAuthenticated()")
+    @Transactional(readOnly = true)
+    public FIQLQueryTO read(final String key) {
+        FIQLQuery fiqlQuery = fiqlQueryDAO.find(key);
+        if (fiqlQuery == null) {
+            LOG.error("Could not find fiqlQuery '" + key + "'");
+            throw new NotFoundException(key);
+        }
+
+        securityChecks(fiqlQuery.getOwner().getUsername());
+
+        return binder.getFIQLQueryTO(fiqlQuery);
+    }
+
+    @PreAuthorize("isAuthenticated()")
+    @Transactional(readOnly = true)
+    public List<FIQLQueryTO> list(final String target) {
+        return fiqlQueryDAO.findByOwner(userDAO.findByUsername(AuthContextUtils.getUsername()), target).stream().
+                map(binder::getFIQLQueryTO).collect(Collectors.toList());
+    }
+
+    @PreAuthorize("isAuthenticated()")
+    public FIQLQueryTO create(final FIQLQueryTO fiqlQueryTO) {
+        return binder.getFIQLQueryTO(fiqlQueryDAO.save(binder.create(fiqlQueryTO)));
+    }
+
+    @PreAuthorize("isAuthenticated()")
+    public FIQLQueryTO update(final FIQLQueryTO fiqlQueryTO) {
+        FIQLQuery fiqlQuery = fiqlQueryDAO.find(fiqlQueryTO.getKey());
+        if (fiqlQuery == null) {
+            LOG.error("Could not find fiqlQuery '" + fiqlQueryTO.getKey() + "'");
+            throw new NotFoundException(fiqlQueryTO.getKey());
+        }
+
+        securityChecks(fiqlQuery.getOwner().getUsername());
+
+        return binder.getFIQLQueryTO(fiqlQueryDAO.save(binder.update(fiqlQuery, fiqlQueryTO)));
+    }
+
+    @PreAuthorize("isAuthenticated()")
+    public FIQLQueryTO delete(final String key) {
+        FIQLQuery fiqlQuery = fiqlQueryDAO.find(key);
+        if (fiqlQuery == null) {
+            LOG.error("Could not find fiqlQuery '" + key + "'");
+            throw new NotFoundException(key);
+        }
+
+        securityChecks(fiqlQuery.getOwner().getUsername());
+
+        FIQLQueryTO deleted = binder.getFIQLQueryTO(fiqlQuery);
+        fiqlQueryDAO.delete(key);
+        return deleted;
+    }
+
+    @Override
+    protected FIQLQueryTO resolveReference(final Method method, final Object... args)
+            throws UnresolvedReferenceException {
+
+        String key = null;
+
+        if (ArrayUtils.isNotEmpty(args)) {
+            for (int i = 0; key == null && i < args.length; i++) {
+                if (args[i] instanceof String) {
+                    key = (String) args[i];
+                } else if (args[i] instanceof FIQLQueryTO) {
+                    key = ((FIQLQueryTO) args[i]).getKey();
+                }
+            }
+        }
+
+        if (key != null) {
+            try {
+                return binder.getFIQLQueryTO(fiqlQueryDAO.find(key));
+            } catch (Throwable ignore) {
+                LOG.debug("Unresolved reference", ignore);
+                throw new UnresolvedReferenceException(ignore);
+            }
+        }
+
+        throw new UnresolvedReferenceException();
+    }
+}
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java
index a6cbdfcf17..1c38c68d3e 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java
@@ -38,6 +38,7 @@ import org.apache.syncope.core.persistence.api.dao.DelegationDAO;
 import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
 import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.dao.FIQLQueryDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
 import org.apache.syncope.core.persistence.api.dao.MailTemplateDAO;
@@ -70,6 +71,7 @@ import org.apache.syncope.core.provisioning.api.data.ApplicationDataBinder;
 import org.apache.syncope.core.provisioning.api.data.AuditDataBinder;
 import org.apache.syncope.core.provisioning.api.data.DelegationDataBinder;
 import org.apache.syncope.core.provisioning.api.data.DynRealmDataBinder;
+import org.apache.syncope.core.provisioning.api.data.FIQLQueryDataBinder;
 import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
 import org.apache.syncope.core.provisioning.api.data.ImplementationDataBinder;
 import org.apache.syncope.core.provisioning.api.data.NotificationDataBinder;
@@ -231,6 +233,16 @@ public class IdRepoLogicContext {
                 auditManager);
     }
 
+    @ConditionalOnMissingBean
+    @Bean
+    public FIQLQueryLogic fiqlQueryLogic(
+            final FIQLQueryDataBinder binder,
+            final UserDAO userDAO,
+            final FIQLQueryDAO fiqlQueryDAO) {
+
+        return new FIQLQueryLogic(binder, fiqlQueryDAO, userDAO);
+    }
+
     @ConditionalOnMissingBean
     @Bean
     public DelegationLogic delegationLogic(
diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/IdRepoRESTCXFContext.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/IdRepoRESTCXFContext.java
index cb7df8718b..398ae2013b 100644
--- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/IdRepoRESTCXFContext.java
+++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/IdRepoRESTCXFContext.java
@@ -54,6 +54,7 @@ import org.apache.syncope.common.rest.api.service.ApplicationService;
 import org.apache.syncope.common.rest.api.service.AuditService;
 import org.apache.syncope.common.rest.api.service.DelegationService;
 import org.apache.syncope.common.rest.api.service.DynRealmService;
+import org.apache.syncope.common.rest.api.service.FIQLQueryService;
 import org.apache.syncope.common.rest.api.service.GroupService;
 import org.apache.syncope.common.rest.api.service.ImplementationService;
 import org.apache.syncope.common.rest.api.service.MailTemplateService;
@@ -78,6 +79,7 @@ import org.apache.syncope.core.logic.ApplicationLogic;
 import org.apache.syncope.core.logic.AuditLogic;
 import org.apache.syncope.core.logic.DelegationLogic;
 import org.apache.syncope.core.logic.DynRealmLogic;
+import org.apache.syncope.core.logic.FIQLQueryLogic;
 import org.apache.syncope.core.logic.GroupLogic;
 import org.apache.syncope.core.logic.ImplementationLogic;
 import org.apache.syncope.core.logic.MailTemplateLogic;
@@ -108,6 +110,7 @@ import org.apache.syncope.core.rest.cxf.service.ApplicationServiceImpl;
 import org.apache.syncope.core.rest.cxf.service.AuditServiceImpl;
 import org.apache.syncope.core.rest.cxf.service.DelegationServiceImpl;
 import org.apache.syncope.core.rest.cxf.service.DynRealmServiceImpl;
+import org.apache.syncope.core.rest.cxf.service.FIQLQueryServiceImpl;
 import org.apache.syncope.core.rest.cxf.service.GroupServiceImpl;
 import org.apache.syncope.core.rest.cxf.service.ImplementationServiceImpl;
 import org.apache.syncope.core.rest.cxf.service.MailTemplateServiceImpl;
@@ -372,6 +375,12 @@ public class IdRepoRESTCXFContext {
         return new AuditServiceImpl(auditLogic);
     }
 
+    @ConditionalOnMissingBean
+    @Bean
+    public FIQLQueryService fiqlQueryService(final FIQLQueryLogic fiqlQueryLogic) {
+        return new FIQLQueryServiceImpl(fiqlQueryLogic);
+    }
+
     @ConditionalOnMissingBean
     @Bean
     public DelegationService delegationService(final DelegationLogic delegationLogic) {
diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/FIQLQueryServiceImpl.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/FIQLQueryServiceImpl.java
new file mode 100644
index 0000000000..1fbe159cb9
--- /dev/null
+++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/FIQLQueryServiceImpl.java
@@ -0,0 +1,67 @@
+/*
+ * 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.core.rest.cxf.service;
+
+import java.net.URI;
+import java.util.List;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.to.FIQLQueryTO;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.FIQLQueryService;
+import org.apache.syncope.core.logic.FIQLQueryLogic;
+import org.springframework.stereotype.Service;
+
+@Service
+public class FIQLQueryServiceImpl extends AbstractService implements FIQLQueryService {
+
+    protected final FIQLQueryLogic logic;
+
+    public FIQLQueryServiceImpl(final FIQLQueryLogic logic) {
+        this.logic = logic;
+    }
+
+    @Override
+    public List<FIQLQueryTO> list(final String target) {
+        return logic.list(target);
+    }
+
+    @Override
+    public FIQLQueryTO read(final String key) {
+        return logic.read(key);
+    }
+
+    @Override
+    public Response create(final FIQLQueryTO applicationTO) {
+        FIQLQueryTO created = logic.create(applicationTO);
+        URI location = uriInfo.getAbsolutePathBuilder().path(created.getKey()).build();
+        return Response.created(location).
+                header(RESTHeaders.RESOURCE_KEY, created.getKey()).
+                build();
+    }
+
+    @Override
+    public void update(final FIQLQueryTO applicationTO) {
+        logic.update(applicationTO);
+    }
+
+    @Override
+    public void delete(final String key) {
+        logic.delete(key);
+    }
+}
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/DelegationDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/DelegationDAO.java
index 09ac3499b5..41e2e65d2a 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/DelegationDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/DelegationDAO.java
@@ -42,5 +42,7 @@ public interface DelegationDAO extends DAO<Delegation> {
 
     Delegation save(Delegation delegation);
 
+    void delete(Delegation delegation);
+
     void delete(String key);
 }
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/DelegationDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/FIQLQueryDAO.java
similarity index 62%
copy from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/DelegationDAO.java
copy to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/FIQLQueryDAO.java
index 09ac3499b5..b76380f8b3 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/DelegationDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/FIQLQueryDAO.java
@@ -19,28 +19,20 @@
 package org.apache.syncope.core.persistence.api.dao;
 
 import java.util.List;
-import java.util.Optional;
-import org.apache.syncope.core.persistence.api.entity.Delegation;
-import org.apache.syncope.core.persistence.api.entity.Role;
+import org.apache.syncope.core.persistence.api.entity.FIQLQuery;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 
-public interface DelegationDAO extends DAO<Delegation> {
+public interface FIQLQueryDAO extends DAO<FIQLQuery> {
 
-    Delegation find(String key);
+    FIQLQuery find(String key);
 
-    Optional<String> findValidFor(String delegating, String delegated);
+    List<FIQLQuery> findByOwner(User user, String target);
 
-    List<String> findValidDelegating(String delegated);
+    List<FIQLQuery> findAll();
 
-    List<Delegation> findByDelegating(User user);
+    FIQLQuery save(FIQLQuery fiqlQuery);
 
-    List<Delegation> findByDelegated(User user);
-
-    List<Delegation> findByRole(Role role);
-
-    List<Delegation> findAll();
-
-    Delegation save(Delegation delegation);
+    void delete(FIQLQuery fiqlQuery);
 
     void delete(String key);
 }
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PushCorrelationRule.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PushCorrelationRule.java
index 256b799f98..c4c354017b 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PushCorrelationRule.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PushCorrelationRule.java
@@ -59,7 +59,7 @@ public interface PushCorrelationRule {
      * @param provision resource provision
      * @return fiql
      */
-    default String getFiql(ConnectorObject connectorObject, Provision provision) {
+    default String getFIQL(ConnectorObject connectorObject, Provision provision) {
         return DEFAULT_FIQL_BUILDER.apply(connectorObject, provision);
     }
 }
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/AbstractPropagationTasks.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/FIQLQuery.java
similarity index 67%
copy from client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/AbstractPropagationTasks.java
copy to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/FIQLQuery.java
index e59c8f2aa9..3023fa9a10 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/AbstractPropagationTasks.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/FIQLQuery.java
@@ -16,14 +16,25 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.client.console.tasks;
+package org.apache.syncope.core.persistence.api.entity;
 
-public abstract class AbstractPropagationTasks extends AbstractTasks {
+import org.apache.syncope.core.persistence.api.entity.user.User;
 
-    private static final long serialVersionUID = -2138947447603621523L;
+public interface FIQLQuery extends Entity {
 
-    public AbstractPropagationTasks(final String id) {
-        super(id);
-    }
+    String getName();
 
+    void setName(String name);
+
+    User getOwner();
+
+    void setOwner(User owner);
+
+    String getTarget();
+
+    void setTarget(String target);
+
+    String getFIQL();
+
+    void setFIQL(String fiql);
 }
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/JPAJSONPersistenceContext.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/JPAJSONPersistenceContext.java
index 3e1c072536..71be82c43b 100644
--- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/JPAJSONPersistenceContext.java
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/JPAJSONPersistenceContext.java
@@ -25,6 +25,7 @@ import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 import org.apache.syncope.core.persistence.api.dao.DelegationDAO;
 import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
 import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
+import org.apache.syncope.core.persistence.api.dao.FIQLQueryDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.JPAJSONAnyDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainAttrDAO;
@@ -130,6 +131,7 @@ public abstract class JPAJSONPersistenceContext {
             final @Lazy RealmDAO realmDAO,
             final @Lazy GroupDAO groupDAO,
             final @Lazy DelegationDAO delegationDAO,
+            final @Lazy FIQLQueryDAO fiqlQueryDAO,
             final @Lazy JPAJSONAnyDAO anyDAO) {
 
         return new JPAJSONUserDAO(
@@ -142,6 +144,7 @@ public abstract class JPAJSONPersistenceContext {
                 realmDAO,
                 groupDAO,
                 delegationDAO,
+                fiqlQueryDAO,
                 securityProperties,
                 anyDAO);
     }
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAJSONUserDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAJSONUserDAO.java
index 2a486d936c..9d61c68015 100644
--- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAJSONUserDAO.java
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAJSONUserDAO.java
@@ -27,6 +27,7 @@ import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO;
 import org.apache.syncope.core.persistence.api.dao.DelegationDAO;
 import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
 import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
+import org.apache.syncope.core.persistence.api.dao.FIQLQueryDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.JPAJSONAnyDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
@@ -56,6 +57,7 @@ public class JPAJSONUserDAO extends JPAUserDAO {
             final RealmDAO realmDAO,
             final GroupDAO groupDAO,
             final DelegationDAO delegationDAO,
+            final FIQLQueryDAO fiqlQueryDAO,
             final SecurityProperties securityProperties,
             final JPAJSONAnyDAO anyDAO) {
 
@@ -68,6 +70,7 @@ public class JPAJSONUserDAO extends JPAUserDAO {
                 realmDAO,
                 groupDAO,
                 delegationDAO,
+                fiqlQueryDAO,
                 securityProperties);
         this.anyDAO = anyDAO;
     }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java
index db45c86024..b2e7b2cc5b 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java
@@ -45,6 +45,7 @@ import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
 import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
 import org.apache.syncope.core.persistence.api.dao.EntityCacheDAO;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.dao.FIQLQueryDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
 import org.apache.syncope.core.persistence.api.dao.MailTemplateDAO;
@@ -100,6 +101,7 @@ import org.apache.syncope.core.persistence.jpa.dao.JPADerSchemaDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPADynRealmDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPAEntityCacheDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPAExternalResourceDAO;
+import org.apache.syncope.core.persistence.jpa.dao.JPAFIQLQueryDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPAGroupDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPAImplementationDAO;
 import org.apache.syncope.core.persistence.jpa.dao.JPAMailTemplateDAO;
@@ -433,6 +435,12 @@ public class PersistenceContext {
         return new JPADelegationDAO();
     }
 
+    @ConditionalOnMissingBean
+    @Bean
+    public FIQLQueryDAO fiqlQueryDAO() {
+        return new JPAFIQLQueryDAO();
+    }
+
     @ConditionalOnMissingBean
     @Bean
     public DerSchemaDAO derSchemaDAO(final @Lazy ExternalResourceDAO resourceDAO) {
@@ -674,7 +682,8 @@ public class PersistenceContext {
             final AccessTokenDAO accessTokenDAO,
             final RealmDAO realmDAO,
             final @Lazy GroupDAO groupDAO,
-            final DelegationDAO delegationDAO) {
+            final DelegationDAO delegationDAO,
+            final FIQLQueryDAO fiqlQueryDAO) {
 
         return new JPAUserDAO(
                 anyUtilsFactory,
@@ -686,6 +695,7 @@ public class PersistenceContext {
                 realmDAO,
                 groupDAO,
                 delegationDAO,
+                fiqlQueryDAO,
                 securityProperties);
     }
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPushCorrelationRule.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPushCorrelationRule.java
index 6cd5115495..fc2dc9f50e 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPushCorrelationRule.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPushCorrelationRule.java
@@ -104,7 +104,7 @@ public class DefaultPushCorrelationRule implements PushCorrelationRule {
     }
 
     @Override
-    public String getFiql(final ConnectorObject connectorObject, final Provision provision) {
+    public String getFIQL(final ConnectorObject connectorObject, final Provision provision) {
         List<CompleteCondition> conditions = new ArrayList<>();
 
         provision.getMapping().getItems().stream().filter(
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADelegationDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADelegationDAO.java
index 7a241532ce..29f44666a2 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADelegationDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPADelegationDAO.java
@@ -104,6 +104,11 @@ public class JPADelegationDAO extends AbstractDAO<Delegation> implements Delegat
         return entityManager().merge(delegation);
     }
 
+    @Override
+    public void delete(final Delegation delegation) {
+        entityManager().remove(delegation);
+    }
+
     @Override
     public void delete(final String key) {
         Delegation delegation = find(key);
@@ -111,6 +116,6 @@ public class JPADelegationDAO extends AbstractDAO<Delegation> implements Delegat
             return;
         }
 
-        entityManager().remove(delegation);
+        delete(delegation);
     }
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAFIQLQueryDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAFIQLQueryDAO.java
new file mode 100644
index 0000000000..9c8a1fb280
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAFIQLQueryDAO.java
@@ -0,0 +1,79 @@
+/*
+ * 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.core.persistence.jpa.dao;
+
+import java.util.List;
+import javax.persistence.TypedQuery;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.core.persistence.api.dao.FIQLQueryDAO;
+import org.apache.syncope.core.persistence.api.entity.FIQLQuery;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.persistence.jpa.entity.JPAFIQLQuery;
+
+public class JPAFIQLQueryDAO extends AbstractDAO<FIQLQuery> implements FIQLQueryDAO {
+
+    @Override
+    public FIQLQuery find(final String key) {
+        return entityManager().find(JPAFIQLQuery.class, key);
+    }
+
+    @Override
+    public List<FIQLQuery> findByOwner(final User user, final String target) {
+        StringBuilder queryString = new StringBuilder("SELECT e FROM ").
+                append(JPAFIQLQuery.class.getSimpleName()).append(" e WHERE e.owner=:user");
+        if (StringUtils.isNotBlank(target)) {
+            queryString.append(" AND e.target=:target");
+        }
+
+        TypedQuery<FIQLQuery> query = entityManager().createQuery(queryString.toString(), FIQLQuery.class);
+        query.setParameter("user", user);
+        if (StringUtils.isNotBlank(target)) {
+            query.setParameter("target", target);
+        }
+
+        return query.getResultList();
+    }
+
+    @Override
+    public List<FIQLQuery> findAll() {
+        TypedQuery<FIQLQuery> query = entityManager().createQuery(
+                "SELECT e FROM " + JPAFIQLQuery.class.getSimpleName() + " e ", FIQLQuery.class);
+        return query.getResultList();
+    }
+
+    @Override
+    public FIQLQuery save(final FIQLQuery fiqlQuery) {
+        return entityManager().merge(fiqlQuery);
+    }
+
+    @Override
+    public void delete(final FIQLQuery fiqlQuery) {
+        entityManager().remove(fiqlQuery);
+    }
+
+    @Override
+    public void delete(final String key) {
+        FIQLQuery fiqlQuery = find(key);
+        if (fiqlQuery == null) {
+            return;
+        }
+
+        delete(fiqlQuery);
+    }
+}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
index fd121dbf6d..8235a98479 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
@@ -41,15 +41,14 @@ import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO;
 import org.apache.syncope.core.persistence.api.dao.DelegationDAO;
 import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
 import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
+import org.apache.syncope.core.persistence.api.dao.FIQLQueryDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
 import org.apache.syncope.core.persistence.api.dao.RoleDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
-import org.apache.syncope.core.persistence.api.entity.AccessToken;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
-import org.apache.syncope.core.persistence.api.entity.Delegation;
 import org.apache.syncope.core.persistence.api.entity.Entity;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.Membership;
@@ -90,6 +89,8 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
 
     protected final DelegationDAO delegationDAO;
 
+    protected final FIQLQueryDAO fiqlQueryDAO;
+
     protected final SecurityProperties securityProperties;
 
     public JPAUserDAO(
@@ -102,6 +103,7 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
             final RealmDAO realmDAO,
             final GroupDAO groupDAO,
             final DelegationDAO delegationDAO,
+            final FIQLQueryDAO fiqlQueryDAO,
             final SecurityProperties securityProperties) {
 
         super(anyUtilsFactory, plainSchemaDAO, derSchemaDAO, dynRealmDAO);
@@ -110,6 +112,7 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
         this.realmDAO = realmDAO;
         this.groupDAO = groupDAO;
         this.delegationDAO = delegationDAO;
+        this.fiqlQueryDAO = fiqlQueryDAO;
         this.securityProperties = securityProperties;
     }
 
@@ -482,17 +485,12 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
         groupDAO.removeDynMemberships(user);
         dynRealmDAO.removeDynMemberships(user.getKey());
 
-        Set<String> delegations = delegationDAO.findByDelegating(user).stream().
-                map(Delegation::getKey).collect(Collectors.toSet());
-        delegations.forEach(delegationDAO::delete);
-        delegations = delegationDAO.findByDelegated(user).stream().
-                map(Delegation::getKey).collect(Collectors.toSet());
-        delegations.forEach(delegationDAO::delete);
+        delegationDAO.findByDelegating(user).forEach(delegationDAO::delete);
+        delegationDAO.findByDelegated(user).forEach(delegationDAO::delete);
 
-        AccessToken accessToken = accessTokenDAO.findByOwner(user.getUsername());
-        if (accessToken != null) {
-            accessTokenDAO.delete(accessToken);
-        }
+        fiqlQueryDAO.findByOwner(user, null).forEach(fiqlQueryDAO::delete);
+
+        Optional.ofNullable(accessTokenDAO.findByOwner(user.getUsername())).ifPresent(accessTokenDAO::delete);
 
         entityManager().remove(user);
     }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADelegation.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADelegation.java
index 4a14b334ae..bb2ce3a3c6 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADelegation.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPADelegation.java
@@ -42,10 +42,10 @@ import org.apache.syncope.core.persistence.jpa.validation.entity.DelegationCheck
 @DelegationCheck
 public class JPADelegation extends AbstractGeneratedKeyEntity implements Delegation {
 
-    public static final String TABLE = "Delegation";
-
     private static final long serialVersionUID = 17988340419552L;
 
+    public static final String TABLE = "Delegation";
+
     @ManyToOne(optional = false)
     private JPAUser delegating;
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
index 374602ede8..4791df4944 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
@@ -35,6 +35,7 @@ import org.apache.syncope.core.persistence.api.entity.DynRealm;
 import org.apache.syncope.core.persistence.api.entity.DynRealmMembership;
 import org.apache.syncope.core.persistence.api.entity.Entity;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.FIQLQuery;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.MailTemplate;
 import org.apache.syncope.core.persistence.api.entity.Notification;
@@ -328,6 +329,8 @@ public class JPAEntityFactory implements EntityFactory {
             result = (E) new JPABatch();
         } else if (reference.equals(Delegation.class)) {
             result = (E) new JPADelegation();
+        } else if (reference.equals(FIQLQuery.class)) {
+            result = (E) new JPAFIQLQuery();
         } else if (reference.equals(SRARoute.class)) {
             result = (E) new JPASRARoute();
         } else if (reference.equals(AuthModule.class)) {
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAFIQLQuery.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAFIQLQuery.java
new file mode 100644
index 0000000000..12ff08969e
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAFIQLQuery.java
@@ -0,0 +1,91 @@
+/*
+ * 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.core.persistence.jpa.entity;
+
+import javax.persistence.Entity;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import javax.validation.constraints.NotNull;
+import org.apache.syncope.core.persistence.api.entity.FIQLQuery;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
+
+@Entity
+@Table(name = JPAFIQLQuery.TABLE, uniqueConstraints =
+        @UniqueConstraint(columnNames = { "owner_id", "name" }))
+public class JPAFIQLQuery extends AbstractGeneratedKeyEntity implements FIQLQuery {
+
+    private static final long serialVersionUID = -8800817340585235280L;
+
+    public static final String TABLE = "FIQLQuery";
+
+    @ManyToOne(optional = false)
+    private JPAUser owner;
+
+    @NotNull
+    private String name;
+
+    @NotNull
+    private String target;
+
+    @NotNull
+    private String fiql;
+
+    @Override
+    public JPAUser getOwner() {
+        return owner;
+    }
+
+    @Override
+    public void setOwner(final User owner) {
+        checkType(owner, JPAUser.class);
+        this.owner = (JPAUser) owner;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String getTarget() {
+        return target;
+    }
+
+    @Override
+    public void setTarget(final String target) {
+        this.target = target;
+    }
+
+    @Override
+    public String getFIQL() {
+        return fiql;
+    }
+
+    @Override
+    public void setFIQL(final String fiql) {
+        this.fiql = fiql;
+    }
+}
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/AbstractPropagationTasks.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/FIQLQueryDataBinder.java
similarity index 68%
copy from client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/AbstractPropagationTasks.java
copy to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/FIQLQueryDataBinder.java
index e59c8f2aa9..dc7f3559e2 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/AbstractPropagationTasks.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/FIQLQueryDataBinder.java
@@ -16,14 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.client.console.tasks;
+package org.apache.syncope.core.provisioning.api.data;
 
-public abstract class AbstractPropagationTasks extends AbstractTasks {
+import org.apache.syncope.common.lib.to.FIQLQueryTO;
+import org.apache.syncope.core.persistence.api.entity.FIQLQuery;
 
-    private static final long serialVersionUID = -2138947447603621523L;
+public interface FIQLQueryDataBinder {
 
-    public AbstractPropagationTasks(final String id) {
-        super(id);
-    }
+    FIQLQuery create(FIQLQueryTO fiqlQueryTO);
 
+    FIQLQuery update(FIQLQuery fiqlQuery, FIQLQueryTO fiqlQueryTO);
+
+    FIQLQueryTO getFIQLQueryTO(FIQLQuery fiqlQuery);
 }
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java
index 70c16be6b3..9ffc0147de 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java
@@ -97,6 +97,7 @@ import org.apache.syncope.core.provisioning.api.data.ClientAppDataBinder;
 import org.apache.syncope.core.provisioning.api.data.ConnInstanceDataBinder;
 import org.apache.syncope.core.provisioning.api.data.DelegationDataBinder;
 import org.apache.syncope.core.provisioning.api.data.DynRealmDataBinder;
+import org.apache.syncope.core.provisioning.api.data.FIQLQueryDataBinder;
 import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
 import org.apache.syncope.core.provisioning.api.data.ImplementationDataBinder;
 import org.apache.syncope.core.provisioning.api.data.NotificationDataBinder;
@@ -137,6 +138,7 @@ import org.apache.syncope.core.provisioning.java.data.ClientAppDataBinderImpl;
 import org.apache.syncope.core.provisioning.java.data.ConnInstanceDataBinderImpl;
 import org.apache.syncope.core.provisioning.java.data.DelegationDataBinderImpl;
 import org.apache.syncope.core.provisioning.java.data.DynRealmDataBinderImpl;
+import org.apache.syncope.core.provisioning.java.data.FIQLQueryDataBinderImpl;
 import org.apache.syncope.core.provisioning.java.data.GroupDataBinderImpl;
 import org.apache.syncope.core.provisioning.java.data.ImplementationDataBinderImpl;
 import org.apache.syncope.core.provisioning.java.data.NotificationDataBinderImpl;
@@ -945,6 +947,16 @@ public class ProvisioningContext {
         return new DelegationDataBinderImpl(userDAO, roleDAO, entityFactory);
     }
 
+    @ConditionalOnMissingBean
+    @Bean
+    public FIQLQueryDataBinder fiqlQueryDataBinder(
+            final SearchCondVisitor searchCondVisitor,
+            final UserDAO userDAO,
+            final EntityFactory entityFactory) {
+
+        return new FIQLQueryDataBinderImpl(searchCondVisitor, userDAO, entityFactory);
+    }
+
     @ConditionalOnMissingBean
     @Bean
     public DynRealmDataBinder dynRealmDataBinder(
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/FIQLQueryDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/FIQLQueryDataBinderImpl.java
new file mode 100644
index 0000000000..a6ae6d457b
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/FIQLQueryDataBinderImpl.java
@@ -0,0 +1,91 @@
+/*
+ * 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.core.provisioning.java.data;
+
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.FIQLQueryTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.FIQLQuery;
+import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
+import org.apache.syncope.core.persistence.api.search.SearchCondVisitor;
+import org.apache.syncope.core.provisioning.api.data.FIQLQueryDataBinder;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FIQLQueryDataBinderImpl implements FIQLQueryDataBinder {
+
+    protected static final Logger LOG = LoggerFactory.getLogger(FIQLQueryDataBinder.class);
+
+    protected final SearchCondVisitor searchCondVisitor;
+
+    protected final UserDAO userDAO;
+
+    protected final EntityFactory entityFactory;
+
+    public FIQLQueryDataBinderImpl(
+            final SearchCondVisitor searchCondVisitor,
+            final UserDAO userDAO,
+            final EntityFactory entityFactory) {
+
+        this.searchCondVisitor = searchCondVisitor;
+        this.userDAO = userDAO;
+        this.entityFactory = entityFactory;
+    }
+
+    @Override
+    public FIQLQuery create(final FIQLQueryTO fiqlQueryTO) {
+        FIQLQuery fiqlQuery = entityFactory.newEntity(FIQLQuery.class);
+
+        fiqlQuery.setOwner(userDAO.findByUsername(AuthContextUtils.getUsername()));
+
+        return update(fiqlQuery, fiqlQueryTO);
+    }
+
+    @Override
+    public FIQLQuery update(final FIQLQuery fiqlQuery, final FIQLQueryTO fiqlQueryTO) {
+        fiqlQuery.setName(fiqlQueryTO.getName());
+        fiqlQuery.setTarget(fiqlQueryTO.getTarget());
+
+        SearchCond cond = SearchCondConverter.convert(searchCondVisitor, fiqlQueryTO.getFiql());
+        if (!cond.isValid()) {
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSearchParameters);
+            sce.getElements().add(fiqlQueryTO.getFiql());
+            throw sce;
+        }
+        fiqlQuery.setFIQL(fiqlQueryTO.getFiql());
+
+        return fiqlQuery;
+    }
+
+    @Override
+    public FIQLQueryTO getFIQLQueryTO(final FIQLQuery fiqlQuery) {
+        FIQLQueryTO fiqlQueryTO = new FIQLQueryTO();
+
+        fiqlQueryTO.setKey(fiqlQuery.getKey());
+        fiqlQueryTO.setName(fiqlQuery.getName());
+        fiqlQueryTO.setTarget(fiqlQuery.getTarget());
+        fiqlQueryTO.setFiql(fiqlQuery.getFIQL());
+
+        return fiqlQueryTO;
+    }
+}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/OutboundMatcher.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/OutboundMatcher.java
index da3b84aae8..33e1c92ebe 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/OutboundMatcher.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/OutboundMatcher.java
@@ -101,7 +101,7 @@ public class OutboundMatcher {
 
     public String getFIQL(final ConnectorObject connectorObject, final Provision provision) {
         return rule(provision).
-                map(rule -> rule.getFiql(connectorObject, provision)).
+                map(rule -> rule.getFIQL(connectorObject, provision)).
                 orElseGet(() -> PushCorrelationRule.DEFAULT_FIQL_BUILDER.apply(connectorObject, provision));
     }
 
diff --git a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/NewBpmnProcess.java b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/NewBpmnProcess.java
index 29cb1d30d8..176a17532f 100644
--- a/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/NewBpmnProcess.java
+++ b/ext/flowable/client-console/src/main/java/org/apache/syncope/client/console/panels/NewBpmnProcess.java
@@ -38,15 +38,13 @@ public class NewBpmnProcess extends TogglePanel<Serializable> {
 
     private static final long serialVersionUID = -4886361549305302161L;
 
-    private final Form<?> form;
-
     public NewBpmnProcess(final String id, final WebMarkupContainer container, final PageReference pageRef) {
         super(id, pageRef);
 
-        form = new Form<>("form");
+        Form<?> form = new Form<>("form");
         addInnerObject(form);
 
-        final TextField<String> key = new TextField<>("key", new Model<>());
+        TextField<String> key = new TextField<>("key", new Model<>());
         key.setRequired(true);
         form.add(key);
 
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/FIQLQueryITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/FIQLQueryITCase.java
new file mode 100644
index 0000000000..f4b003af94
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/FIQLQueryITCase.java
@@ -0,0 +1,87 @@
+/*
+ * 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.fit.core;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.List;
+import javax.ws.rs.core.Response;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.FIQLQueryTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.service.FIQLQueryService;
+import org.apache.syncope.fit.AbstractITCase;
+import org.junit.jupiter.api.Test;
+
+public class FIQLQueryITCase extends AbstractITCase {
+
+    @Test
+    public void crud() {
+        FIQLQueryService fiqlQueryService =
+                CLIENT_FACTORY.create("bellini", "password").getService(FIQLQueryService.class);
+
+        int before = fiqlQueryService.list(AnyTypeKind.USER.name()).size();
+
+        FIQLQueryTO query = new FIQLQueryTO();
+        query.setFiql("INVALID");
+        query.setName("fancy name");
+        query.setTarget(AnyTypeKind.USER.name());
+
+        try {
+            fiqlQueryService.create(query);
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.InvalidSearchParameters, e.getType());
+        }
+
+        query.setFiql("username=~*one*");
+        Response response = fiqlQueryService.create(query);
+        String key = response.getHeaderString(RESTHeaders.RESOURCE_KEY);
+        assertNotNull(key);
+
+        query = fiqlQueryService.read(key);
+        assertNotNull(query.getKey());
+        assertEquals("username=~*one*", query.getFiql());
+        assertEquals("fancy name", query.getName());
+
+        List<FIQLQueryTO> queries = fiqlQueryService.list(AnyTypeKind.USER.name());
+        assertEquals(before + 1, queries.size());
+        assertTrue(queries.stream().anyMatch(q -> key.equals(q.getKey())));
+
+        query.setFiql("email==ciao@bao.it");
+        query.setName("not so fancy");
+        fiqlQueryService.update(query);
+
+        query = fiqlQueryService.read(key);
+        assertEquals("email==ciao@bao.it", query.getFiql());
+        assertEquals("not so fancy", query.getName());
+
+        fiqlQueryService.delete(key);
+
+        queries = fiqlQueryService.list(AnyTypeKind.USER.name());
+        assertEquals(before, queries.size());
+        assertFalse(queries.contains(query));
+    }
+}
diff --git a/pom.xml b/pom.xml
index dc4aa30d3e..39a6681d4a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -426,11 +426,11 @@ under the License.
 
     <flowable.version>6.7.2</flowable.version>
 
-    <camel.version>3.17.0</camel.version>
+    <camel.version>3.18.0</camel.version>
 
     <slf4j.version>1.7.36</slf4j.version>
 
-    <elasticsearch.version>8.3.1</elasticsearch.version>
+    <elasticsearch.version>8.3.2</elasticsearch.version>
 
     <apacheds.version>2.0.0.AM26</apacheds.version>
     <apachedirapi.version>2.0.0</apachedirapi.version>