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 2020/02/13 11:37:44 UTC

[syncope] branch master updated: [SYNCOPE-1542] Cleanup and refactoring

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 0c18912  [SYNCOPE-1542] Cleanup and refactoring
0c18912 is described below

commit 0c1891200ef2c07597fee7986e160e0f5daceee0
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Thu Feb 13 12:23:27 2020 +0100

    [SYNCOPE-1542] Cleanup and refactoring
---
 .../syncope/client/ui/commons/Constants.java       |   6 +
 .../ui/commons/markup/html/form/FieldPanel.java    |   2 +-
 .../syncope/client/console/panels/AnyPanel.java    |  30 +--
 .../console/panels/search/AbstractSearchPanel.java |  17 +-
 .../panels/search/AnyObjectSearchPanel.java        |  15 +-
 .../console/panels/search/GroupSearchPanel.java    |   6 +-
 .../console/panels/search/SearchClausePanel.java   | 202 ++++++++-------------
 .../syncope/client/console/wizards/any/Groups.java |   6 +-
 .../syncope/client/console/wizards/any/Roles.java  |   7 +-
 .../client/console/panels/AnyPanel.properties      |   2 +-
 .../console/panels/AnyPanel_fr_CA.properties       |   2 +-
 .../client/console/panels/AnyPanel_it.properties   |   2 +-
 .../client/console/panels/AnyPanel_ja.properties   |   2 +-
 .../console/panels/AnyPanel_pt_BR.properties       |   2 +-
 .../client/console/panels/AnyPanel_ru.properties   |   3 +-
 .../api/PropagationByResourceTest.java             |  62 ++++---
 16 files changed, 175 insertions(+), 191 deletions(-)

diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/Constants.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/Constants.java
index b106b46..bb8b52f 100644
--- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/Constants.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/Constants.java
@@ -57,6 +57,8 @@ public final class Constants {
 
     public static final String ON_CHANGE = "change";
 
+    public static final String ON_KEYUP = "keyup";
+
     public static final String ON_KEYDOWN = "keydown";
 
     public static final String ON_BLUR = "blur";
@@ -105,6 +107,10 @@ public final class Constants {
 
     public static final String NOT_FOUND_ICON = "glyphicon glyphicon-remove-circle";
 
+    public static final int MAX_GROUP_LIST_SIZE = 30;
+
+    public static final int MAX_ROLE_LIST_SIZE = 30;
+
     public static final String NOTIFICATION_MSG_PARAM = "notificationMessage";
 
     public static final String NOTIFICATION_LEVEL_PARAM = "notificationLevel";
diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/FieldPanel.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/FieldPanel.java
index d618694..663d2a5 100644
--- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/FieldPanel.java
+++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/markup/html/form/FieldPanel.java
@@ -236,7 +236,7 @@ public abstract class FieldPanel<T extends Serializable> extends AbstractFieldPa
 
     protected PageReference getPageReference() {
         // SYNCOPE-1213
-        // default implementation does not requier to pass page reference, override this method of want otherwise
+        // default implementation does not require to pass page reference, override this method of want otherwise
         return null;
     }
 }
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 8e2ce8e..bf0d26c 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
@@ -57,7 +57,7 @@ import org.apache.wicket.markup.html.WebMarkupContainer;
 import org.apache.wicket.markup.html.WebPage;
 import org.apache.wicket.markup.html.panel.Panel;
 import org.apache.wicket.model.Model;
-import org.apache.wicket.model.StringResourceModel;
+import org.apache.wicket.model.ResourceModel;
 import org.apache.wicket.model.util.ListModel;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -206,19 +206,19 @@ public class AnyPanel extends Panel implements ModalPanel {
         // ------------------------
         // Accordion
         // ------------------------
-        final Model<Integer> model = Model.of(-1);
-        final StringResourceModel searchResult = new StringResourceModel("search.result", this, new Model<>(anyTypeTO));
-        final Accordion accordion = new Accordion("accordionPanel", List.of(new AbstractTab(searchResult) {
+        Model<Integer> model = Model.of(-1);
+        Accordion accordion = new Accordion("accordionPanel",
+                List.of(new AbstractTab(new ResourceModel("search.result")) {
 
-            protected static final long serialVersionUID = 1037272333056449377L;
+                    protected static final long serialVersionUID = 1037272333056449377L;
 
-            @Override
-            public WebMarkupContainer getPanel(final String panelId) {
-                searchPanel = getSearchPanel(panelId);
-                return searchPanel;
-            }
+                    @Override
+                    public WebMarkupContainer getPanel(final String panelId) {
+                        searchPanel = getSearchPanel(panelId);
+                        return searchPanel;
+                    }
 
-        }), model) {
+                }), model) {
 
             protected static final long serialVersionUID = -3056452800492734900L;
 
@@ -238,7 +238,7 @@ public class AnyPanel extends Panel implements ModalPanel {
                     public void onClick(final AjaxRequestTarget target) {
                         model.setObject(model.getObject() == 0 ? -1 : 0);
                     }
-                }.setBody(searchResult);
+                }.setBody(tab.getTitle()).setEscapeModelStrings(false);
             }
         };
         accordion.setOutputMarkupId(true);
@@ -319,7 +319,7 @@ public class AnyPanel extends Panel implements ModalPanel {
                 clause.setProperty("username");
 
                 panel = new UserSearchPanel.Builder(
-                        new ListModel<>(clauses)).required(true).enableSearch().build(id);
+                        new ListModel<>(clauses)).realm(realmTO.getFullPath()).required(true).enableSearch().build(id);
                 break;
 
             case GROUP:
@@ -328,7 +328,7 @@ public class AnyPanel extends Panel implements ModalPanel {
                 clause.setProperty("name");
 
                 panel = new GroupSearchPanel.Builder(
-                        new ListModel<>(clauses)).required(true).enableSearch().build(id);
+                        new ListModel<>(clauses)).realm(realmTO.getFullPath()).required(true).enableSearch().build(id);
                 break;
 
             case ANY_OBJECT:
@@ -337,7 +337,7 @@ public class AnyPanel extends Panel implements ModalPanel {
                 clause.setProperty("name");
 
                 panel = new AnyObjectSearchPanel.Builder(anyTypeTO.getKey(),
-                        new ListModel<>(clauses)).required(true).enableSearch().build(id);
+                        new ListModel<>(clauses)).realm(realmTO.getFullPath()).required(true).enableSearch().build(id);
                 break;
 
             default:
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 87b2a17..b3cc3d2b 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
@@ -54,7 +54,7 @@ public abstract class AbstractSearchPanel extends Panel {
 
     protected IModel<List<SearchClause.Type>> types;
 
-    protected IModel<Map<String, String>> groupNames;
+    protected IModel<List<String>> groupNames;
 
     protected IModel<List<String>> roleNames;
 
@@ -64,6 +64,8 @@ public abstract class AbstractSearchPanel extends Panel {
 
     protected WebMarkupContainer searchFormContainer;
 
+    protected final String realm;
+
     protected final AnyTypeKind typeKind;
 
     protected final String type;
@@ -80,6 +82,8 @@ public abstract class AbstractSearchPanel extends Panel {
 
         protected final IModel<List<SearchClause>> model;
 
+        protected String realm = SyncopeConstants.ROOT_REALM;
+
         protected boolean required = true;
 
         protected boolean enableSearch = false;
@@ -96,6 +100,11 @@ public abstract class AbstractSearchPanel extends Panel {
             this.model = model;
         }
 
+        public Builder<T> realm(final String realm) {
+            this.realm = realm;
+            return this;
+        }
+
         public Builder<T> enableSearch(final IEventSink resultContainer) {
             this.resultContainer = resultContainer;
             return enableSearch();
@@ -128,12 +137,13 @@ public abstract class AbstractSearchPanel extends Panel {
 
         super(id);
         populate();
-        Pair<IModel<Map<String, String>>, Integer> groupInfo =
+        Pair<IModel<List<String>>, Integer> groupInfo =
                 SyncopeConsoleSession.get().owns(IdRepoEntitlement.GROUP_SEARCH)
                 ? Pair.of(groupNames, groupRestClient.count(SyncopeConstants.ROOT_REALM, null, null))
                 : Pair.of(groupNames, 0);
 
         this.model = builder.model;
+        this.realm = builder.realm;
         this.typeKind = kind;
         this.type = type;
         this.required = builder.required;
@@ -147,6 +157,7 @@ public abstract class AbstractSearchPanel extends Panel {
 
         SearchClausePanel searchClausePanel = new SearchClausePanel("panel", "panel",
                 Model.of(new SearchClause()),
+                realm,
                 required,
                 types,
                 builder.customizer,
@@ -156,7 +167,7 @@ public abstract class AbstractSearchPanel extends Panel {
             searchClausePanel.enableSearch(builder.resultContainer);
         }
 
-        final MultiFieldPanel.Builder<SearchClause> searchView = new MultiFieldPanel.Builder<SearchClause>(model) {
+        MultiFieldPanel.Builder<SearchClause> searchView = new MultiFieldPanel.Builder<SearchClause>(model) {
 
             private static final long serialVersionUID = 1343431509987473047L;
 
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 d88e83b..e371ca7 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,7 +25,7 @@ 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.common.lib.SyncopeConstants;
+import org.apache.syncope.client.ui.commons.Constants;
 import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.common.lib.to.PlainSchemaTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
@@ -38,8 +38,6 @@ public class AnyObjectSearchPanel extends AbstractSearchPanel {
 
     private static final long serialVersionUID = -1769527800450203738L;
 
-    public static final int MAX_GROUP_LIST_CARDINALITY = 30;
-
     public static class Builder extends AbstractSearchPanel.Builder<AnyObjectSearchPanel> {
 
         private static final long serialVersionUID = 6308997285778809578L;
@@ -79,18 +77,19 @@ public class AnyObjectSearchPanel extends AbstractSearchPanel {
             }
         };
 
-        this.groupNames = new LoadableDetachableModel<Map<String, String>>() {
+        this.groupNames = new LoadableDetachableModel<List<String>>() {
 
             private static final long serialVersionUID = 5275935387613157437L;
 
             @Override
-            protected Map<String, String> load() {
-                return groupRestClient.search(SyncopeConstants.ROOT_REALM,
+            protected List<String> load() {
+                return groupRestClient.search(
+                        realm,
                         null,
                         1,
-                        MAX_GROUP_LIST_CARDINALITY,
+                        Constants.MAX_GROUP_LIST_SIZE,
                         new SortParam<>("name", true),
-                        null).stream().collect(Collectors.toMap(GroupTO::getKey, GroupTO::getName));
+                        null).stream().map(GroupTO::getName).collect(Collectors.toList());
             }
         };
 
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 1021875..1dfeb5e 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
@@ -71,13 +71,13 @@ public class GroupSearchPanel extends AbstractSearchPanel {
             }
         };
 
-        this.groupNames = new LoadableDetachableModel<Map<String, String>>() {
+        this.groupNames = new LoadableDetachableModel<List<String>>() {
 
             private static final long serialVersionUID = 5275935387613157437L;
 
             @Override
-            protected Map<String, String> load() {
-                return Map.of();
+            protected List<String> load() {
+                return List.of();
             }
         };
 
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 db6441d..1153467 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
@@ -25,7 +25,9 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.function.Consumer;
 import java.util.stream.Collectors;
+import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.client.ui.commons.Constants;
@@ -40,7 +42,6 @@ import org.apache.syncope.client.ui.commons.markup.html.form.AjaxDropDownChoiceP
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
 import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel;
 import org.apache.syncope.client.lib.SyncopeClient;
-import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.common.lib.to.PlainSchemaTO;
 import org.apache.syncope.common.lib.to.RelationshipTypeTO;
@@ -71,6 +72,21 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
 
     private static final long serialVersionUID = -527351923968737757L;
 
+    protected static final AttributeModifier PREVENT_DEFAULT_RETURN = AttributeModifier.replace(
+            "onkeydown",
+            Model.of("if (event.keyCode == 13) { event.preventDefault(); }"));
+
+    protected static final Consumer<AjaxRequestAttributes> AJAX_SUBMIT_ON_RETURN =
+            attributes -> attributes.getAjaxCallListeners().add(new AjaxCallListener() {
+
+                private static final long serialVersionUID = 7160235486520935153L;
+
+                @Override
+                public CharSequence getPrecondition(final Component component) {
+                    return "return (Wicket.Event.keyCode(attrs.event) == 13);";
+                }
+            });
+
     public interface Customizer extends Serializable {
 
         default IChoiceRenderer<SearchClause.Type> typeRenderer() {
@@ -108,6 +124,8 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
         }
     }
 
+    private final String realm;
+
     private final boolean required;
 
     private final IModel<List<SearchClause.Type>> types;
@@ -118,7 +136,7 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
 
     private final IModel<List<String>> dnames;
 
-    private final Pair<IModel<Map<String, String>>, Integer> groupInfo;
+    private final Pair<IModel<List<String>>, Integer> groupInfo;
 
     private final IModel<List<String>> roleNames;
 
@@ -146,12 +164,13 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
             final String id,
             final String name,
             final Model<SearchClause> clause,
+            final String realm,
             final boolean required,
             final IModel<List<SearchClause.Type>> types,
             final Customizer customizer,
             final IModel<Map<String, PlainSchemaTO>> anames,
             final IModel<List<String>> dnames,
-            final Pair<IModel<Map<String, String>>, Integer> groupInfo,
+            final Pair<IModel<List<String>>, Integer> groupInfo,
             final IModel<List<String>> roleNames,
             final IModel<List<String>> privilegeNames,
             final IModel<List<String>> resourceNames) {
@@ -160,6 +179,7 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
 
         this.clause = clause == null ? new Model<>(null) : clause;
 
+        this.realm = realm;
         this.required = required;
         this.types = types;
         this.customizer = customizer;
@@ -255,8 +275,7 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
                         return names.stream().sorted().collect(Collectors.toList());
 
                     case GROUP_MEMBERSHIP:
-                        return groupInfo.getLeft().getObject().values().stream().
-                                sorted().collect(Collectors.toList());
+                        return groupInfo.getLeft().getObject();
 
                     case ROLE_MEMBERSHIP:
                         return roleNames.getObject().stream().
@@ -289,11 +308,8 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
         this.searchButton.setEnabled(true);
         this.searchButton.setVisible(true);
 
-        field.add(AttributeModifier.replace(
-                "onkeydown",
-                Model.of("if(event.keyCode == 13) {event.preventDefault();}")));
-
-        field.add(new AjaxEventBehavior("keydown") {
+        field.add(PREVENT_DEFAULT_RETURN);
+        field.add(new AjaxEventBehavior(Constants.ON_KEYDOWN) {
 
             private static final long serialVersionUID = -7133385027739964990L;
 
@@ -309,16 +325,7 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
             @Override
             protected void updateAjaxAttributes(final AjaxRequestAttributes attributes) {
                 super.updateAjaxAttributes(attributes);
-
-                attributes.getAjaxCallListeners().add(new AjaxCallListener() {
-
-                    private static final long serialVersionUID = 7160235486520935153L;
-
-                    @Override
-                    public CharSequence getPrecondition(final Component component) {
-                        return "if (Wicket.Event.keyCode(attrs.event)  == 13) { return true; } else { return false; }";
-                    }
-                });
+                AJAX_SUBMIT_ON_RETURN.accept(attributes);
             }
         });
     }
@@ -354,13 +361,13 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
 
     @Override
     public FieldPanel<SearchClause> settingsDependingComponents() {
-        final SearchClause searchClause = this.clause.getObject();
+        SearchClause searchClause = this.clause.getObject();
 
-        final WebMarkupContainer operatorContainer = new WebMarkupContainer("operatorContainer");
+        WebMarkupContainer operatorContainer = new WebMarkupContainer("operatorContainer");
         operatorContainer.setOutputMarkupId(true);
         field.add(operatorContainer);
 
-        final BootstrapToggleConfig config = new BootstrapToggleConfig().
+        BootstrapToggleConfig config = new BootstrapToggleConfig().
                 withOnStyle(BootstrapToggleConfig.Style.info).
                 withOffStyle(BootstrapToggleConfig.Style.warning).
                 withSize(BootstrapToggleConfig.Size.mini);
@@ -394,7 +401,7 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
 
             @Override
             protected CheckBox newCheckBox(final String id, final IModel<Boolean> model) {
-                final CheckBox checkBox = super.newCheckBox(id, model);
+                CheckBox checkBox = super.newCheckBox(id, model);
                 checkBox.add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
 
                     private static final long serialVersionUID = 1L;
@@ -413,76 +420,71 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
             operatorContainer.add(searchButtonFragment);
         }
 
-        final AjaxTextFieldPanel property = new AjaxTextFieldPanel(
-                "property",
-                "property",
-                new PropertyModel<>(searchClause, "property"),
-                false);
+        AjaxTextFieldPanel property = new AjaxTextFieldPanel(
+                "property", "property", new PropertyModel<>(searchClause, "property"), false);
         property.hideLabel().setOutputMarkupId(true).setEnabled(true);
         property.setChoices(properties.getObject());
         field.add(property);
 
-        property.getField().add(AttributeModifier.replace(
-                "onkeydown",
-                Model.of("if(event.keyCode == 13) { event.preventDefault(); }")));
-
-        property.getField().add(new IndicatorAjaxEventBehavior("onkeyup") {
+        property.getField().add(PREVENT_DEFAULT_RETURN);
+        property.getField().add(new IndicatorAjaxEventBehavior(Constants.ON_KEYUP) {
 
-            private static final long serialVersionUID = -7866120562087857309L;
+            private static final long serialVersionUID = -957948639666058749L;
 
             @Override
             protected void onEvent(final AjaxRequestTarget target) {
-                if (field.getModel().getObject() == null || field.getModel().getObject().getType() == null) {
-                    return;
-                }
+                if (field.getModel().getObject() != null
+                        && field.getModel().getObject().getType() == Type.GROUP_MEMBERSHIP) {
 
-                if (field.getModel().getObject().getType() == Type.GROUP_MEMBERSHIP) {
                     String[] inputAsArray = property.getField().getInputAsArray();
-
-                    if (StringUtils.isBlank(property.getField().getInput()) || inputAsArray.length == 0) {
+                    if (ArrayUtils.isEmpty(inputAsArray)) {
                         property.setChoices(properties.getObject());
-                    } else {
-                        String inputValue = (inputAsArray.length > 1 && inputAsArray[1] != null)
+                    } else if (groupInfo.getRight() > Constants.MAX_GROUP_LIST_SIZE) {
+                        String inputValue = inputAsArray.length > 1 && inputAsArray[1] != null
                                 ? inputAsArray[1]
                                 : property.getField().getInput();
-                        inputValue = (inputValue.startsWith("*") && !inputValue.endsWith("*"))
-                                ? inputValue + '*'
-                                : (!inputValue.startsWith("*") && inputValue.endsWith("*"))
-                                ? '*' + inputValue
-                                : (inputValue.startsWith("*") && inputValue.endsWith("*")
-                                ? inputValue : '*' + inputValue + '*');
-
-                        if (groupInfo.getRight() > AnyObjectSearchPanel.MAX_GROUP_LIST_CARDINALITY) {
-                            property.setChoices(groupRestClient.search(
-                                    SyncopeConstants.ROOT_REALM,
-                                    SyncopeClient.getGroupSearchConditionBuilder().
-                                            is("name").equalToIgnoreCase(inputValue).
-                                            query(),
-                                    1,
-                                    AnyObjectSearchPanel.MAX_GROUP_LIST_CARDINALITY,
-                                    new SortParam<>("name", true),
-                                    null).stream().map(GroupTO::getName).collect(Collectors.toList()));
+                        if (!inputValue.startsWith("*")) {
+                            inputValue = "*" + inputValue;
                         }
+                        if (!inputValue.endsWith("*")) {
+                            inputValue = inputValue + "*";
+                        }
+                        property.setChoices(groupRestClient.search(
+                                realm,
+                                SyncopeClient.getGroupSearchConditionBuilder().
+                                        is("name").equalToIgnoreCase(inputValue).
+                                        query(),
+                                1,
+                                Constants.MAX_GROUP_LIST_SIZE,
+                                new SortParam<>("name", true),
+                                null).stream().map(GroupTO::getName).collect(Collectors.toList()));
                     }
                 }
             }
+        });
+        property.getField().add(new IndicatorAjaxEventBehavior(Constants.ON_KEYDOWN) {
+
+            private static final long serialVersionUID = -7133385027739964990L;
 
             @Override
-            protected void updateAjaxAttributes(final AjaxRequestAttributes attributes) {
-                super.updateAjaxAttributes(attributes);
-                attributes.getAjaxCallListeners().clear();
+            protected void onEvent(final AjaxRequestTarget target) {
+                target.focusComponent(null);
+                property.getField().inputChanged();
+                property.getField().validate();
+                if (property.getField().isValid()) {
+                    property.getField().valid();
+                    property.getField().updateModel();
+                }
             }
-        }, new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) {
-
-            private static final long serialVersionUID = -1107858522700306810L;
 
             @Override
-            protected void onUpdate(final AjaxRequestTarget target) {
-
+            protected void updateAjaxAttributes(final AjaxRequestAttributes attributes) {
+                super.updateAjaxAttributes(attributes);
+                AJAX_SUBMIT_ON_RETURN.accept(attributes);
             }
         });
 
-        final AjaxDropDownChoicePanel<SearchClause.Comparator> comparator = new AjaxDropDownChoicePanel<>(
+        AjaxDropDownChoicePanel<SearchClause.Comparator> comparator = new AjaxDropDownChoicePanel<>(
                 "comparator", "comparator", new PropertyModel<>(searchClause, "comparator"));
         comparator.setChoices(comparators);
         comparator.setNullValid(false).hideLabel().setOutputMarkupId(true);
@@ -490,16 +492,13 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
         comparator.setChoiceRenderer(getComparatorRender(field.getModel()));
         field.add(comparator);
 
-        final AjaxTextFieldPanel value = new AjaxTextFieldPanel(
+        AjaxTextFieldPanel value = new AjaxTextFieldPanel(
                 "value", "value", new PropertyModel<>(searchClause, "value"), false);
         value.hideLabel().setOutputMarkupId(true);
         field.add(value);
 
-        value.getField().add(AttributeModifier.replace(
-                "onkeydown",
-                Model.of("if(event.keyCode == 13) {event.preventDefault();}")));
-
-        value.getField().add(new IndicatorAjaxEventBehavior("keydown") {
+        value.getField().add(PREVENT_DEFAULT_RETURN);
+        value.getField().add(new IndicatorAjaxEventBehavior(Constants.ON_KEYDOWN) {
 
             private static final long serialVersionUID = -7133385027739964990L;
 
@@ -517,16 +516,7 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
             @Override
             protected void updateAjaxAttributes(final AjaxRequestAttributes attributes) {
                 super.updateAjaxAttributes(attributes);
-
-                attributes.getAjaxCallListeners().add(new AjaxCallListener() {
-
-                    private static final long serialVersionUID = 7160235486520935153L;
-
-                    @Override
-                    public CharSequence getPrecondition(final Component component) {
-                        return "if (Wicket.Event.keyCode(attrs.event)  == 13) { return true; } else { return false; }";
-                    }
-                });
+                AJAX_SUBMIT_ON_RETURN.accept(attributes);
             }
         });
 
@@ -896,7 +886,7 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
     @Override
     public FieldPanel<SearchClause> clone() {
         SearchClausePanel panel = new SearchClausePanel(
-                getId(), name, null, required, types, customizer, anames, dnames, groupInfo,
+                getId(), name, null, realm, required, types, customizer, anames, dnames, groupInfo,
                 roleNames, privilegeNames, resourceNames);
         panel.setReadOnly(this.isReadOnly());
         panel.setRequired(this.isRequired());
@@ -906,46 +896,6 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
         return panel;
     }
 
-    private static class DefaultChoiceRender implements IChoiceRenderer<String> {
-
-        private static final long serialVersionUID = -8034248752951761058L;
-
-        @Override
-        public Object getDisplayValue(final String object) {
-            return object;
-        }
-
-        @Override
-        public String getIdValue(final String object, final int index) {
-            return object;
-        }
-
-        @Override
-        public String getObject(final String id, final IModel<? extends List<? extends String>> choices) {
-            return id;
-        }
-    }
-
-    private class GroupChoiceRender extends DefaultChoiceRender {
-
-        private static final long serialVersionUID = -8034248752951761058L;
-
-        @Override
-        public String getIdValue(final String object, final int index) {
-            return object;
-        }
-
-        @Override
-        public String getObject(final String id, final IModel<? extends List<? extends String>> choices) {
-            return id;
-        }
-
-        @Override
-        public Object getDisplayValue(final String object) {
-            return groupInfo.getLeft().getObject().get(object);
-        }
-    }
-
     public static class SearchEvent implements Serializable {
 
         private static final long serialVersionUID = 2693338614198749301L;
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Groups.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Groups.java
index 96d4792..7aec7e9 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Groups.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Groups.java
@@ -155,7 +155,7 @@ public class Groups extends AbstractGroups {
                                     anyTO.getRealm(),
                                     SyncopeClient.getGroupSearchConditionBuilder().
                                             isAssignable().and().is("name").equalTo(filter).query(),
-                                    1, MAX_GROUP_LIST_CARDINALITY,
+                                    1, Constants.MAX_GROUP_LIST_SIZE,
                                     new SortParam<>("name", true),
                                     null)).stream().map(input -> new MembershipTO.Builder(input.getKey())
                             .groupName(input.getName()).build()).collect(Collectors.toList());
@@ -215,7 +215,7 @@ public class Groups extends AbstractGroups {
         }
 
         /**
-         * Retrieve the first MAX_GROUP_LIST_CARDINALITY assignable.
+         * Retrieve the first MAX_GROUP_LIST_SIZE assignable.
          */
         @Override
         protected void reloadObject() {
@@ -223,7 +223,7 @@ public class Groups extends AbstractGroups {
                     realm,
                     SyncopeClient.getGroupSearchConditionBuilder().isAssignable().query(),
                     1,
-                    MAX_GROUP_LIST_CARDINALITY,
+                    Constants.MAX_GROUP_LIST_SIZE,
                     new SortParam<>("name", true),
                     null);
         }
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Roles.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Roles.java
index f1fd279..ceda3a0 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Roles.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Roles.java
@@ -27,6 +27,7 @@ import org.apache.commons.collections4.ListUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.console.SyncopeWebApplication;
 import org.apache.syncope.client.console.rest.RoleRestClient;
+import org.apache.syncope.client.ui.commons.Constants;
 import org.apache.syncope.client.ui.commons.ajax.markup.html.LabelInfo;
 import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel;
 import org.apache.syncope.common.lib.to.AnyTO;
@@ -46,8 +47,6 @@ public class Roles extends WizardStep implements ICondition {
 
     private static final long serialVersionUID = 552437609667518888L;
 
-    private static final int MAX_ROLE_LIST_SIZE = 30;
-
     private final List<String> allRoles;
 
     protected WebMarkupContainer dynrolesContainer;
@@ -96,8 +95,8 @@ public class Roles extends WizardStep implements ICondition {
                     @Override
                     public List<String> execute(final String filter) {
                         if (StringUtils.isEmpty(filter) || "*".equals(filter)) {
-                            return allRoles.size() > MAX_ROLE_LIST_SIZE
-                                    ? allRoles.subList(0, MAX_ROLE_LIST_SIZE)
+                            return allRoles.size() > Constants.MAX_ROLE_LIST_SIZE
+                                    ? allRoles.subList(0, Constants.MAX_ROLE_LIST_SIZE)
                                     : allRoles;
 
                         }
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel.properties
index f9235eb..62834fb 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel.properties
@@ -14,4 +14,4 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-search.result=${key} Search
+search.result=<i class="fa fa-caret-down" aria-hidden="true"></i> Search
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel_fr_CA.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel_fr_CA.properties
index d6bdf75..8907de4 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel_fr_CA.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel_fr_CA.properties
@@ -14,4 +14,4 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-search.result=Recherche ${key} 
+search.result=<i class="fa fa-caret-down" aria-hidden="true"></i> Recherche
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel_it.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel_it.properties
index eede918..6cbb1c6 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel_it.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel_it.properties
@@ -14,4 +14,4 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-search.result=Ricerca ${key}
+search.result=<i class="fa fa-caret-down" aria-hidden="true"></i> Ricerca
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel_ja.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel_ja.properties
index 03ebd78..fa7f13c 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel_ja.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel_ja.properties
@@ -14,4 +14,4 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-search.result=${key} \u691c\u7d22
+search.result=<i class="fa fa-caret-down" aria-hidden="true"></i> \u691c\u7d22
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel_pt_BR.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel_pt_BR.properties
index f9235eb..05fd988 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel_pt_BR.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel_pt_BR.properties
@@ -14,4 +14,4 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-search.result=${key} Search
+search.result=<i class="fa fa-caret-down" aria-hidden="true"></i> Procurar
diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel_ru.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel_ru.properties
index b427cb7..9122092 100644
--- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel_ru.properties
+++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/panels/AnyPanel_ru.properties
@@ -14,5 +14,4 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-#
-search.result=\u041f\u043e\u0438\u0441\u043a \u0434\u043b\u044f ${key}
+search.result=<i class="fa fa-caret-down" aria-hidden="true"></i> \u041f\u043e\u0438\u0441\u043a \u0434\u043b\u044f
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/PropagationByResourceTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/PropagationByResourceTest.java
index 65e6228..47aeb21 100644
--- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/PropagationByResourceTest.java
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/PropagationByResourceTest.java
@@ -30,6 +30,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mock;
@@ -37,7 +38,7 @@ import org.springframework.test.util.ReflectionTestUtils;
 
 public class PropagationByResourceTest extends AbstractTest {
 
-    private final String key = "testKey";
+    private static final String KEY = "testKey";
 
     private final PropagationByResource<String> propagationByResource = new PropagationByResource<>();
 
@@ -48,6 +49,7 @@ public class PropagationByResourceTest extends AbstractTest {
             @Mock Set<String> toBeDeleted,
             @Mock Map<String, String> oldConnObjectKeys,
             @Mock PropagationByResource<String> propByRes) {
+
         ReflectionTestUtils.setField(propagationByResource, "toBeCreated", toBeCreated);
         ReflectionTestUtils.setField(propagationByResource, "toBeUpdated", toBeUpdated);
         ReflectionTestUtils.setField(propagationByResource, "toBeDeleted", toBeDeleted);
@@ -75,20 +77,20 @@ public class PropagationByResourceTest extends AbstractTest {
         verify(toBeDeleted).addAll(any());
 
         String oldConnObjectKey = "oldConnObjectKey";
-        propagationByResource.addOldConnObjectKey(key, oldConnObjectKey);
-        verify(oldConnObjectKeys).put(key, oldConnObjectKey);
-        propagationByResource.addOldConnObjectKey(key, null);
-        verify(oldConnObjectKeys, times(0)).put(key, null);
+        propagationByResource.addOldConnObjectKey(KEY, oldConnObjectKey);
+        verify(oldConnObjectKeys).put(KEY, oldConnObjectKey);
+        propagationByResource.addOldConnObjectKey(KEY, null);
+        verify(oldConnObjectKeys, times(0)).put(KEY, null);
         propagationByResource.addOldConnObjectKey(null, null);
         verify(oldConnObjectKeys, times(0)).put(null, null);
     }
 
     @Test
     public void add() {
-        assertTrue(propagationByResource.add(ResourceOperation.CREATE, key));
-        assertTrue(propagationByResource.add(ResourceOperation.UPDATE, key));
-        assertTrue(propagationByResource.add(ResourceOperation.DELETE, key));
-        assertFalse(propagationByResource.add(ResourceOperation.NONE, key));
+        assertTrue(propagationByResource.add(ResourceOperation.CREATE, KEY));
+        assertTrue(propagationByResource.add(ResourceOperation.UPDATE, KEY));
+        assertTrue(propagationByResource.add(ResourceOperation.DELETE, KEY));
+        assertFalse(propagationByResource.add(ResourceOperation.NONE, KEY));
     }
 
     @Test
@@ -105,10 +107,10 @@ public class PropagationByResourceTest extends AbstractTest {
 
     @Test
     public void remove() {
-        assertFalse(propagationByResource.remove(ResourceOperation.CREATE, key));
-        assertFalse(propagationByResource.remove(ResourceOperation.UPDATE, key));
-        assertFalse(propagationByResource.remove(ResourceOperation.DELETE, key));
-        assertFalse(propagationByResource.remove(ResourceOperation.NONE, key));
+        assertFalse(propagationByResource.remove(ResourceOperation.CREATE, KEY));
+        assertFalse(propagationByResource.remove(ResourceOperation.UPDATE, KEY));
+        assertFalse(propagationByResource.remove(ResourceOperation.DELETE, KEY));
+        assertFalse(propagationByResource.remove(ResourceOperation.NONE, KEY));
     }
 
     @Test
@@ -135,23 +137,23 @@ public class PropagationByResourceTest extends AbstractTest {
 
     @Test
     public void contains() {
-        assertFalse(propagationByResource.contains(ResourceOperation.CREATE, key));
-        assertFalse(propagationByResource.contains(ResourceOperation.UPDATE, key));
-        assertFalse(propagationByResource.contains(ResourceOperation.DELETE, key));
-        assertFalse(propagationByResource.contains(ResourceOperation.NONE, key));
+        assertFalse(propagationByResource.contains(ResourceOperation.CREATE, KEY));
+        assertFalse(propagationByResource.contains(ResourceOperation.UPDATE, KEY));
+        assertFalse(propagationByResource.contains(ResourceOperation.DELETE, KEY));
+        assertFalse(propagationByResource.contains(ResourceOperation.NONE, KEY));
 
         Set<String> matchingList = new HashSet<>();
-        matchingList.add(key);
-        assertFalse(propagationByResource.contains(key));
+        matchingList.add(KEY);
+        assertFalse(propagationByResource.contains(KEY));
 
         ReflectionTestUtils.setField(propagationByResource, "toBeDeleted", matchingList);
-        assertTrue(propagationByResource.contains(key));
+        assertTrue(propagationByResource.contains(KEY));
     }
 
     @Test
     public void get() {
         Set<String> matchingList = new HashSet<>();
-        matchingList.add(key);
+        matchingList.add(KEY);
 
         ReflectionTestUtils.setField(propagationByResource, "toBeDeleted", matchingList);
         assertEquals(matchingList, propagationByResource.get(ResourceOperation.DELETE));
@@ -181,4 +183,22 @@ public class PropagationByResourceTest extends AbstractTest {
         propagationByResource.set(ResourceOperation.DELETE, keys);
         assertEquals(keys, ReflectionTestUtils.getField(propagationByResource, "toBeDeleted"));
     }
+
+    @Test
+    public void byLinkedAccount() {
+        PropagationByResource<Pair<String, String>> propByLinkedAccount = new PropagationByResource<>();
+        propByLinkedAccount.add(ResourceOperation.CREATE, Pair.of("resource1", "connObjectKey1"));
+        propByLinkedAccount.add(ResourceOperation.CREATE, Pair.of("resource2", "connObjectKey2"));
+
+        assertEquals(2, propByLinkedAccount.asMap().size());
+        assertEquals(ResourceOperation.CREATE, propByLinkedAccount.asMap().get(Pair.of("resource1", "connObjectKey1")));
+        assertEquals(ResourceOperation.CREATE, propByLinkedAccount.asMap().get(Pair.of("resource2", "connObjectKey2")));
+
+        Set<String> noPropResourceKeys = Set.of("resource2");
+        propByLinkedAccount.get(ResourceOperation.CREATE).
+                removeIf(account -> noPropResourceKeys.contains(account.getLeft()));
+
+        assertEquals(1, propByLinkedAccount.asMap().size());
+        assertEquals(ResourceOperation.CREATE, propByLinkedAccount.asMap().get(Pair.of("resource1", "connObjectKey1")));
+    }
 }