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/05/24 06:37:50 UTC

[syncope] branch master updated: [SYNCOPE-1679] Adding support for $auxClasses (#347)

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 ccb30f53bd [SYNCOPE-1679] Adding support for $auxClasses (#347)
ccb30f53bd is described below

commit ccb30f53bdb9bf46daafb1052495149aeefee648
Author: Francesco Chicchiriccò <il...@users.noreply.github.com>
AuthorDate: Tue May 24 08:37:46 2022 +0200

    [SYNCOPE-1679] Adding support for $auxClasses (#347)
---
 .../console/panels/search/AbstractSearchPanel.java | 20 +++++-
 .../panels/search/AnyObjectSearchPanel.java        |  1 +
 .../console/panels/search/GroupSearchPanel.java    |  1 +
 .../client/console/panels/search/SearchClause.java |  1 +
 .../console/panels/search/SearchClausePanel.java   | 13 +++-
 .../client/console/panels/search/SearchUtils.java  | 11 ++++
 .../console/panels/search/UserSearchPanel.java     |  5 +-
 .../search/AbstractFiqlSearchConditionBuilder.java | 40 +++++++++---
 .../syncope/common/lib/search/SpecialAttr.java     |  6 +-
 .../common/lib/search/SyncopeFiqlParser.java       | 20 ++----
 .../lib/search/SyncopeFiqlSearchCondition.java     |  1 -
 .../syncope/common/lib/search/SyncopeProperty.java | 50 +++++++++++++--
 .../persistence/api/dao/search/AuxClassCond.java   | 74 ++++++++++++++++++++++
 .../persistence/api/search/SearchCondVisitor.java  |  7 ++
 .../api/search/FilterConverterTest.java            | 20 ++++++
 .../api/search/SearchCondConverterTest.java        | 13 ++++
 .../persistence/jpa/dao/PGJPAJSONAnySearchDAO.java | 25 ++++++++
 .../src/main/resources/myjson/views.xml            | 18 ++++++
 .../src/main/resources/pgjsonb/views.xml           | 18 ++++++
 .../core/persistence/jpa/dao/JPAAnySearchDAO.java  | 28 ++++++++
 .../core/persistence/jpa/dao/SearchSupport.java    |  4 ++
 .../src/main/resources/sqlserver_views.xml         | 18 ++++++
 core/persistence-jpa/src/main/resources/views.xml  | 18 ++++++
 .../core/persistence/jpa/inner/AnySearchTest.java  | 11 ++++
 .../elasticsearch/client/ElasticsearchUtils.java   |  2 +
 .../jpa/dao/ElasticsearchAnySearchDAO.java         | 13 ++++
 .../org/apache/syncope/fit/core/SearchITCase.java  | 18 +++++-
 src/main/asciidoc/reference-guide/usage/core.adoc  |  7 ++
 28 files changed, 427 insertions(+), 36 deletions(-)

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 5cf2392356..1b624b5e6d 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
@@ -22,13 +22,16 @@ import java.io.Serializable;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.client.console.SyncopeWebApplication;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
+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.MultiFieldPanel;
 import org.apache.syncope.common.lib.SyncopeConstants;
 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;
@@ -51,6 +54,8 @@ public abstract class AbstractSearchPanel extends Panel {
 
     protected IModel<Map<String, PlainSchemaTO>> anames;
 
+    protected IModel<List<String>> auxClassNames;
+
     protected IModel<List<String>> resourceNames;
 
     protected IModel<List<SearchClause.Type>> types;
@@ -159,7 +164,7 @@ public abstract class AbstractSearchPanel extends Panel {
                 required,
                 types,
                 builder.customizer,
-                anames, dnames, groupInfo, roleNames, privilegeNames, resourceNames);
+                anames, dnames, groupInfo, roleNames, privilegeNames, auxClassNames, resourceNames);
 
         if (enableSearch) {
             searchClausePanel.enableSearch(builder.resultContainer);
@@ -195,6 +200,19 @@ public abstract class AbstractSearchPanel extends Panel {
             }
         };
 
+        auxClassNames = new LoadableDetachableModel<>() {
+
+            private static final long serialVersionUID = 5275935387613157437L;
+
+            @Override
+            protected List<String> load() {
+                return AnyTypeClassRestClient.list().stream().
+                        filter(c -> c.getInUseByTypes().isEmpty()).
+                        map(AnyTypeClassTO::getKey).
+                        collect(Collectors.toList());
+            }
+        };
+
         resourceNames = new LoadableDetachableModel<>() {
 
             private static final long serialVersionUID = 5275935387613157437L;
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 80235038aa..282059878f 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
@@ -112,6 +112,7 @@ public class AnyObjectSearchPanel extends AbstractSearchPanel {
         List<SearchClause.Type> result = new ArrayList<>();
         result.add(SearchClause.Type.ATTRIBUTE);
         result.add(SearchClause.Type.GROUP_MEMBERSHIP);
+        result.add(SearchClause.Type.AUX_CLASS);
         result.add(SearchClause.Type.RESOURCE);
         result.add(SearchClause.Type.RELATIONSHIP);
         return result;
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 b912f238b7..a7ae709ad6 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
@@ -66,6 +66,7 @@ public class GroupSearchPanel extends AbstractSearchPanel {
             protected List<SearchClause.Type> load() {
                 List<SearchClause.Type> result = new ArrayList<>();
                 result.add(SearchClause.Type.ATTRIBUTE);
+                result.add(SearchClause.Type.AUX_CLASS);
                 result.add(SearchClause.Type.RESOURCE);
                 result.add(SearchClause.Type.GROUP_MEMBER);
                 return result;
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClause.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClause.java
index 69f2d153c5..1c84c5a301 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClause.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchClause.java
@@ -41,6 +41,7 @@ public final class SearchClause implements Serializable {
         GROUP_MEMBER,
         ROLE_MEMBERSHIP,
         PRIVILEGE,
+        AUX_CLASS,
         RESOURCE,
         RELATIONSHIP,
         CUSTOM;
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 79fdac669a..1996a97693 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
@@ -150,6 +150,8 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
 
     private final IModel<List<String>> privilegeNames;
 
+    private final IModel<List<String>> auxClassNames;
+
     private final IModel<List<String>> resourceNames;
 
     private IModel<SearchClause> clause;
@@ -183,6 +185,7 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
             final Pair<IModel<List<String>>, IModel<Integer>> groupInfo,
             final IModel<List<String>> roleNames,
             final IModel<List<String>> privilegeNames,
+            final IModel<List<String>> auxClassNames,
             final IModel<List<String>> resourceNames) {
 
         super(id, name, clause);
@@ -197,6 +200,7 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
         this.groupInfo = groupInfo;
         this.roleNames = roleNames;
         this.privilegeNames = privilegeNames;
+        this.auxClassNames = auxClassNames;
         this.resourceNames = resourceNames;
 
         searchButton = new AjaxLink<>("search") {
@@ -239,6 +243,7 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
                     case ATTRIBUTE:
                         return List.of(SearchClause.Comparator.values());
 
+                    case AUX_CLASS:
                     case ROLE_MEMBERSHIP:
                     case PRIVILEGE:
                     case GROUP_MEMBERSHIP:
@@ -293,6 +298,10 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
                         return privilegeNames.getObject().stream().
                                 sorted().collect(Collectors.toList());
 
+                    case AUX_CLASS:
+                        return auxClassNames.getObject().stream().
+                                sorted().collect(Collectors.toList());
+
                     case RESOURCE:
                         return resourceNames.getObject().stream().
                                 sorted().collect(Collectors.toList());
@@ -647,6 +656,7 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
                     property.setModelObject(StringUtils.EMPTY);
                     break;
 
+                case AUX_CLASS:
                 case RESOURCE:
                     value.setEnabled(false);
                     value.setModelObject(StringUtils.EMPTY);
@@ -759,6 +769,7 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
                         }
                         break;
 
+                    case AUX_CLASS:
                     case ROLE_MEMBERSHIP:
                     case PRIVILEGE:
                     case RESOURCE:
@@ -1026,7 +1037,7 @@ public class SearchClausePanel extends FieldPanel<SearchClause> {
     public FieldPanel<SearchClause> clone() {
         SearchClausePanel panel = new SearchClausePanel(
                 getId(), name, null, required, types, customizer, anames, dnames, groupInfo,
-                roleNames, privilegeNames, resourceNames);
+                roleNames, privilegeNames, auxClassNames, resourceNames);
         panel.setReadOnly(this.isReadOnly());
         panel.setRequired(this.isRequired());
         if (searchButton.isEnabled()) {
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 57e8a60389..89efc1de18 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
@@ -149,6 +149,9 @@ public final class SearchUtils implements Serializable {
         } else if (SpecialAttr.GROUPS.toString().equals(property)) {
             clause.setType(SearchClause.Type.GROUP_MEMBERSHIP);
             clause.setProperty(value);
+        } else if (SpecialAttr.AUX_CLASSES.toString().equals(property)) {
+            clause.setType(SearchClause.Type.AUX_CLASS);
+            clause.setProperty(value);
         } else if (SpecialAttr.RESOURCES.toString().equals(property)) {
             clause.setType(SearchClause.Type.RESOURCE);
             clause.setProperty(value);
@@ -277,6 +280,14 @@ public final class SearchUtils implements Serializable {
                         }
                         break;
 
+                    case AUX_CLASS:
+                        if (StringUtils.isNotBlank(clause.getProperty())) {
+                            condition = clause.getComparator() == SearchClause.Comparator.EQUALS
+                                    ? builder.hasAuxClasses(clause.getProperty())
+                                    : builder.hasNotAuxClasses(clause.getProperty());
+                        }
+                        break;
+
                     case RESOURCE:
                         if (StringUtils.isNotBlank(clause.getProperty())) {
                             condition = clause.getComparator() == SearchClause.Comparator.EQUALS
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 9b38f4d0fa..47b2a0e41a 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
@@ -71,8 +71,8 @@ public class UserSearchPanel extends AnyObjectSearchPanel {
             @Override
             protected List<String> load() {
                 return ApplicationRestClient.list().stream().
-                    flatMap(application -> application.getPrivileges().stream()).
-                    map(EntityTO::getKey).collect(Collectors.toList());
+                        flatMap(application -> application.getPrivileges().stream()).
+                        map(EntityTO::getKey).collect(Collectors.toList());
             }
         };
     }
@@ -84,6 +84,7 @@ public class UserSearchPanel extends AnyObjectSearchPanel {
         result.add(SearchClause.Type.ROLE_MEMBERSHIP);
         result.add(SearchClause.Type.PRIVILEGE);
         result.add(SearchClause.Type.GROUP_MEMBERSHIP);
+        result.add(SearchClause.Type.AUX_CLASS);
         result.add(SearchClause.Type.RESOURCE);
         result.add(SearchClause.Type.RELATIONSHIP);
         return result;
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/AbstractFiqlSearchConditionBuilder.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/AbstractFiqlSearchConditionBuilder.java
index 7f36d562d7..fbeb54a36a 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/AbstractFiqlSearchConditionBuilder.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/AbstractFiqlSearchConditionBuilder.java
@@ -82,6 +82,14 @@ public abstract class AbstractFiqlSearchConditionBuilder<
                 notInDynRealms(dynRealm, moreDynRealms);
     }
 
+    public C hasAuxClasses(final String auxClass, final String... moreAuxClasses) {
+        return newBuilderInstance().is(SpecialAttr.AUX_CLASSES.toString()).hasAuxClasses(auxClass, moreAuxClasses);
+    }
+
+    public C hasNotAuxClasses(final String auxClass, final String... moreAuxClasses) {
+        return newBuilderInstance().is(SpecialAttr.AUX_CLASSES.toString()).hasNotAuxClasses(auxClass, moreAuxClasses);
+    }
+
     public C hasResources(final String resource, final String... moreResources) {
         return newBuilderInstance().is(SpecialAttr.RESOURCES.toString()).hasResources(resource, moreResources);
     }
@@ -121,6 +129,16 @@ public abstract class AbstractFiqlSearchConditionBuilder<
             return (C) this;
         }
 
+        @Override
+        public C equalToIgnoreCase(final String value, final String... moreValues) {
+            return condition(SyncopeFiqlParser.IEQ, value, (Object[]) moreValues);
+        }
+
+        @Override
+        public C notEqualTolIgnoreCase(final String literalOrPattern) {
+            return condition(SyncopeFiqlParser.NIEQ, literalOrPattern);
+        }
+
         @Override
         public C nullValue() {
             return condition(FiqlParser.EQ, SpecialAttr.NULL);
@@ -132,25 +150,27 @@ public abstract class AbstractFiqlSearchConditionBuilder<
         }
 
         @Override
-        public C hasResources(final String resource, final String... moreResources) {
-            this.result = SpecialAttr.RESOURCES.toString();
-            return condition(FiqlParser.EQ, resource, (Object[]) moreResources);
+        public C hasAuxClasses(final String auxClass, final String... moreAuxClasses) {
+            this.result = SpecialAttr.AUX_CLASSES.toString();
+            return condition(FiqlParser.EQ, auxClass, (Object[]) moreAuxClasses);
         }
 
         @Override
-        public C hasNotResources(final String resource, final String... moreResources) {
-            this.result = SpecialAttr.RESOURCES.toString();
-            return condition(FiqlParser.NEQ, resource, (Object[]) moreResources);
+        public C hasNotAuxClasses(final String auxClass, final String... moreAuxClasses) {
+            this.result = SpecialAttr.AUX_CLASSES.toString();
+            return condition(FiqlParser.NEQ, auxClass, (Object[]) moreAuxClasses);
         }
 
         @Override
-        public C equalToIgnoreCase(final String value, final String... moreValues) {
-            return condition(SyncopeFiqlParser.IEQ, value, (Object[]) moreValues);
+        public C hasResources(final String resource, final String... moreResources) {
+            this.result = SpecialAttr.RESOURCES.toString();
+            return condition(FiqlParser.EQ, resource, (Object[]) moreResources);
         }
 
         @Override
-        public C notEqualTolIgnoreCase(final String literalOrPattern) {
-            return condition(SyncopeFiqlParser.NIEQ, literalOrPattern);
+        public C hasNotResources(final String resource, final String... moreResources) {
+            this.result = SpecialAttr.RESOURCES.toString();
+            return condition(FiqlParser.NEQ, resource, (Object[]) moreResources);
         }
 
         @Override
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java
index e6d34140be..3bd37291bc 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/SpecialAttr.java
@@ -22,7 +22,7 @@ import java.util.Arrays;
 import java.util.Optional;
 
 public enum SpecialAttr {
-    
+
     /**
      * Applies to users, groups and any objects.
      */
@@ -31,6 +31,10 @@ public enum SpecialAttr {
      * Applies to any objects.
      */
     TYPE("$type"),
+    /**
+     * Applies to users, groups and any objects.
+     */
+    AUX_CLASSES("$auxClasses"),
     /**
      * Applies to users, groups and any objects.
      */
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/SyncopeFiqlParser.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/SyncopeFiqlParser.java
index e7e0191a75..01f01c8c2b 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/SyncopeFiqlParser.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/SyncopeFiqlParser.java
@@ -19,10 +19,10 @@
 package org.apache.syncope.common.lib.search;
 
 import java.util.Map;
+import java.util.Optional;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import org.apache.commons.lang3.StringUtils;
-
 import org.apache.cxf.jaxrs.ext.search.ConditionType;
 import org.apache.cxf.jaxrs.ext.search.SearchBean;
 import org.apache.cxf.jaxrs.ext.search.SearchCondition;
@@ -77,18 +77,13 @@ public class SyncopeFiqlParser<T> extends FiqlParser<T> {
                 throw new SearchParseException("Not a comparison expression: " + expr);
             }
 
-            String name = unwrapSetter(propertyName);
-
-            name = getActualSetterName(name);
-            TypeInfoObject castedValue = parseType(propertyName, name, value);
-            if (castedValue != null) {
-                return new SyncopeComparison(name, operator, castedValue);
-            } else {
-                return null;
-            }
-        } else {
-            throw new SearchParseException("Not a comparison expression: " + expr);
+            String name = getActualSetterName(unwrapSetter(propertyName));
+            return Optional.ofNullable(parseType(propertyName, name, value)).
+                    map(typeInfoObject -> new SyncopeComparison(name, operator, typeInfoObject)).
+                    orElse(null);
         }
+
+        throw new SearchParseException("Not a comparison expression: " + expr);
     }
 
     private class SyncopeComparison implements ASTNode<T> {
@@ -148,5 +143,4 @@ public class SyncopeFiqlParser<T> extends FiqlParser<T> {
             }
         }
     }
-
 }
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/SyncopeFiqlSearchCondition.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/SyncopeFiqlSearchCondition.java
index 41e0bacff2..31fe477c68 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/SyncopeFiqlSearchCondition.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/SyncopeFiqlSearchCondition.java
@@ -55,5 +55,4 @@ public class SyncopeFiqlSearchCondition<T> extends SimpleSearchCondition<T> {
     public String getOperator() {
         return operator;
     }
-
 }
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/SyncopeProperty.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/SyncopeProperty.java
index e792826d6c..3b40a90b8c 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/SyncopeProperty.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/SyncopeProperty.java
@@ -28,7 +28,8 @@ import org.apache.cxf.jaxrs.ext.search.client.Property;
  */
 public interface SyncopeProperty<C extends SyncopeCompleteCondition<?, ?>> extends Property {
 
-    /** Is textual property equal to (ignoring case) given literal or matching given pattern?
+    /**
+     * Is textual property equal to (ignoring case) given literal or matching given pattern?
      *
      * @param value first value
      * @param moreValues more values
@@ -36,26 +37,48 @@ public interface SyncopeProperty<C extends SyncopeCompleteCondition<?, ?>> exten
      */
     C equalToIgnoreCase(String value, String... moreValues);
 
-    /** Is textual property different (ignoring case) than given literal or not matching given pattern?
+    /**
+     * Is textual property different (ignoring case) than given literal or not matching given pattern?
      *
      * @param literalOrPattern The literal or Pattern String
      * @return updated condition
      */
     C notEqualTolIgnoreCase(String literalOrPattern);
 
-    /** Is property null?
+    /**
+     * Is property null?
      *
      * @return updated condition
      */
     C nullValue();
 
-    /** Is property not null?
+    /**
+     * Is property not null?
      *
      * @return updated condition
      */
     C notNullValue();
 
-    /** Is user, group or any object owning given resource(s)?
+    /**
+     * Has user, group or any object assigned the given auxiliary class(es)?
+     *
+     * @param auxClass first auxiliary class
+     * @param moreAuxClasses more auxiliary classes
+     * @return updated condition
+     */
+    C hasAuxClasses(String auxClass, String... moreAuxClasses);
+
+    /**
+     * Has user, group or any object not assigned the given auxiliary class(es)?
+     *
+     * @param auxClass first auxiliary class
+     * @param moreAuxClasses more auxiliary classes
+     * @return updated condition
+     */
+    C hasNotAuxClasses(String auxClass, String... moreAuxClasses);
+
+    /**
+     * Is user, group or any object owning given resource(s)?
      *
      * @param resource first resource
      * @param moreResources more resources
@@ -63,7 +86,8 @@ public interface SyncopeProperty<C extends SyncopeCompleteCondition<?, ?>> exten
      */
     C hasResources(String resource, String... moreResources);
 
-    /** Is user, group or any object not owning given resource(s)?
+    /**
+     * Is user, group or any object not owning given resource(s)?
      *
      * @param resource first resource
      * @param moreResources more resources
@@ -71,7 +95,21 @@ public interface SyncopeProperty<C extends SyncopeCompleteCondition<?, ?>> exten
      */
     C hasNotResources(String resource, String... moreResources);
 
+    /**
+     * Is user, group or any object in the given dynamic realm(s)?
+     *
+     * @param dynRealm first dynamic realm
+     * @param moreDynRealms more dynamic realms
+     * @return updated condition
+     */
     C inDynRealms(String dynRealm, String... moreDynRealms);
 
+    /**
+     * Is user, group or any object not in the given dynamic realm(s)?
+     *
+     * @param dynRealm first dynamic realm
+     * @param moreDynRealms more dynamic realms
+     * @return updated condition
+     */
     C notInDynRealms(String dynRealm, String... moreDynRealms);
 }
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/AuxClassCond.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/AuxClassCond.java
new file mode 100644
index 0000000000..b479fd43b8
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/AuxClassCond.java
@@ -0,0 +1,74 @@
+/*
+ * 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.api.dao.search;
+
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+public class AuxClassCond extends AbstractSearchCond {
+
+    private static final long serialVersionUID = 4298076973281246633L;
+
+    private String auxClass;
+
+    public String getAuxClass() {
+        return auxClass;
+    }
+
+    public void setAuxClass(final String auxClass) {
+        this.auxClass = auxClass;
+    }
+
+    @Override
+    public boolean isValid() {
+        return auxClass != null;
+    }
+
+    @Override
+    public int hashCode() {
+        return new HashCodeBuilder().
+                append(auxClass).
+                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 AuxClassCond other = (AuxClassCond) obj;
+        return new EqualsBuilder().
+                append(auxClass, other.auxClass).
+                build();
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(this).
+                append(auxClass).
+                build();
+    }
+}
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java
index 011627e68c..ddd027d027 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java
@@ -41,6 +41,7 @@ import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
 import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
 import org.apache.syncope.core.persistence.api.dao.search.AttrCond;
+import org.apache.syncope.core.persistence.api.dao.search.AuxClassCond;
 import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond;
 import org.apache.syncope.core.persistence.api.dao.search.MemberCond;
 import org.apache.syncope.core.persistence.api.dao.search.PrivilegeCond;
@@ -151,6 +152,12 @@ public class SearchCondVisitor extends AbstractSearchConditionVisitor<SearchBean
                             leaf = SearchCond.getLeaf(typeCond);
                             break;
 
+                        case AUX_CLASSES:
+                            AuxClassCond auxClassCond = new AuxClassCond();
+                            auxClassCond.setAuxClass(value);
+                            leaf = SearchCond.getLeaf(auxClassCond);
+                            break;
+                            
                         case RESOURCES:
                             ResourceCond resourceCond = new ResourceCond();
                             resourceCond.setResourceKey(value);
diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/FilterConverterTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/FilterConverterTest.java
index 84f73d846f..353ae27bb7 100644
--- a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/FilterConverterTest.java
+++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/FilterConverterTest.java
@@ -192,6 +192,26 @@ public class FilterConverterTest {
         }
     }
 
+    @Test
+    public void hasAuxClasses() {
+        try {
+            FilterConverter.convert(SpecialAttr.AUX_CLASSES + "==clazz1");
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.InvalidSearchParameters, e.getType());
+        }
+    }
+
+    @Test
+    public void hasNotAuxClasses() {
+        try {
+            FilterConverter.convert(SpecialAttr.AUX_CLASSES + "!=clazz1");
+            fail();
+        } catch (SyncopeClientException e) {
+            assertEquals(ClientExceptionType.InvalidSearchParameters, e.getType());
+        }
+    }
+
     @Test
     public void hasResources() {
         try {
diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java
index 111aa6774b..7bee68e818 100644
--- a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java
+++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java
@@ -33,6 +33,7 @@ import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
 import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
 import org.apache.syncope.core.persistence.api.dao.search.AttrCond;
+import org.apache.syncope.core.persistence.api.dao.search.AuxClassCond;
 import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond;
 import org.apache.syncope.core.persistence.api.dao.search.MemberCond;
 import org.apache.syncope.core.persistence.api.dao.search.PrivilegeCond;
@@ -226,6 +227,18 @@ public class SearchCondConverterTest {
         assertEquals(leaf, SearchCondConverter.convert(VISITOR, fiql));
     }
 
+    @Test
+    public void auxClasses() {
+        String fiql = new UserFiqlSearchConditionBuilder().hasAuxClasses("clazz1").query();
+        assertEquals(SpecialAttr.AUX_CLASSES + "==clazz1", fiql);
+
+        AuxClassCond cond = new AuxClassCond();
+        cond.setAuxClass("clazz1");
+        SearchCond leaf = SearchCond.getLeaf(cond);
+
+        assertEquals(leaf, SearchCondConverter.convert(VISITOR, fiql));
+    }
+
     @Test
     public void resources() {
         String fiql = new UserFiqlSearchConditionBuilder().hasResources("resource-ldap").query();
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java
index 3a4191d766..24e8f8b6ce 100644
--- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAnySearchDAO.java
@@ -43,6 +43,7 @@ import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
 import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
 import org.apache.syncope.core.persistence.api.dao.search.AttrCond;
+import org.apache.syncope.core.persistence.api.dao.search.AuxClassCond;
 import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond;
 import org.apache.syncope.core.persistence.api.dao.search.MemberCond;
 import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
@@ -297,6 +298,30 @@ public class PGJPAJSONAnySearchDAO extends JPAAnySearchDAO {
         return query.toString();
     }
 
+    @Override
+    protected String getQuery(
+            final AuxClassCond cond,
+            final boolean not,
+            final List<Object> parameters,
+            final SearchSupport svs) {
+
+        StringBuilder query = new StringBuilder();
+
+        if (not) {
+            query.append("id NOT IN (");
+        } else {
+            query.append("id IN (");
+        }
+
+        query.append("SELECT DISTINCT any_id FROM ").
+                append(svs.auxClass().name).
+                append(" WHERE anyTypeClass_id=?").
+                append(setParameter(parameters, cond.getAuxClass())).
+                append(')');
+
+        return query.toString();
+    }
+
     @Override
     protected String getQuery(
             final RoleCond cond,
diff --git a/core/persistence-jpa-json/src/main/resources/myjson/views.xml b/core/persistence-jpa-json/src/main/resources/myjson/views.xml
index 8e7da4d7ea..1744dc06d9 100644
--- a/core/persistence-jpa-json/src/main/resources/myjson/views.xml
+++ b/core/persistence-jpa-json/src/main/resources/myjson/views.xml
@@ -96,6 +96,12 @@ under the License.
     FROM DynRoleMembers drm, SyncopeRole_Privilege rp
     WHERE drm.role_id = rp.role_id
   </entry>
+  <entry key="user_search_auxClass">
+    CREATE VIEW user_search_auxClass AS
+
+    SELECT st.user_id AS any_id, st.anyTypeClass_id AS anyTypeClass_id
+    FROM SyncopeUser_AnyTypeClass st
+  </entry>
   <entry key="user_search_resource">
     CREATE VIEW user_search_resource AS
 
@@ -140,6 +146,12 @@ under the License.
     FROM AMembership m, SyncopeGroup g
     WHERE m.group_id = g.id
   </entry>
+  <entry key="anyObject_search_auxClass">
+    CREATE VIEW anyObject_search_auxClass AS
+
+    SELECT st.anyObject_id AS any_id, st.anyTypeClass_id AS anyTypeClass_id
+    FROM AnyObject_AnyTypeClass st
+  </entry>
   <entry key="anyObject_search_resource">
     CREATE VIEW anyObject_search_resource AS
 
@@ -171,6 +183,12 @@ under the License.
     attrUniqueValue JSON PATH '$.uniqueValue')
     ) AS attrs
   </entry>
+  <entry key="group_search_auxClass">
+    CREATE VIEW group_search_auxClass AS
+
+    SELECT st.group_id AS any_id, st.anyTypeClass_id AS anyTypeClass_id
+    FROM SyncopeGroup_AnyTypeClass st
+  </entry>
   <entry key="group_search_resource">
     CREATE VIEW group_search_resource AS
 
diff --git a/core/persistence-jpa-json/src/main/resources/pgjsonb/views.xml b/core/persistence-jpa-json/src/main/resources/pgjsonb/views.xml
index e360813225..e25da2f66f 100644
--- a/core/persistence-jpa-json/src/main/resources/pgjsonb/views.xml
+++ b/core/persistence-jpa-json/src/main/resources/pgjsonb/views.xml
@@ -80,6 +80,12 @@ under the License.
     FROM DynRoleMembers drm, SyncopeRole_Privilege rp
     WHERE drm.role_id = rp.role_id
   </entry>
+  <entry key="user_search_auxClass">
+    CREATE VIEW user_search_auxClass AS
+
+    SELECT st.user_id AS any_id, st.anyTypeClass_id AS anyTypeClass_id
+    FROM SyncopeUser_AnyTypeClass st
+  </entry>
   <entry key="user_search_resource">
     CREATE VIEW user_search_resource AS
 
@@ -108,6 +114,12 @@ under the License.
     FROM AMembership m, SyncopeGroup g
     WHERE m.group_id = g.id
   </entry>
+  <entry key="anyObject_search_auxClass">
+    CREATE VIEW anyObject_search_auxClass AS
+
+    SELECT st.anyObject_id AS any_id, st.anyTypeClass_id AS anyTypeClass_id
+    FROM AnyObject_AnyTypeClass st
+  </entry>
   <entry key="anyObject_search_resource">
     CREATE VIEW anyObject_search_resource AS
 
@@ -123,6 +135,12 @@ under the License.
   </entry>
 
   <!-- group -->
+  <entry key="group_search_auxClass">
+    CREATE VIEW group_search_auxClass AS
+
+    SELECT st.group_id AS any_id, st.anyTypeClass_id AS anyTypeClass_id
+    FROM SyncopeGroup_AnyTypeClass st
+  </entry>
   <entry key="group_search_resource">
     CREATE VIEW group_search_resource AS
 
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
index 08374986bf..f7dacda6fd 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
@@ -51,6 +51,7 @@ import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
 import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
+import org.apache.syncope.core.persistence.api.dao.search.AuxClassCond;
 import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond;
 import org.apache.syncope.core.persistence.api.dao.search.MemberCond;
 import org.apache.syncope.core.persistence.api.dao.search.PrivilegeCond;
@@ -542,6 +543,9 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
                         filter(leaf -> AnyTypeKind.ANY_OBJECT == svs.anyTypeKind).
                         ifPresent(leaf -> query.append(getQuery(leaf, not, parameters, svs)));
 
+                cond.getLeaf(AuxClassCond.class).
+                        ifPresent(leaf -> query.append(getQuery(leaf, not, parameters, svs)));
+
                 cond.getLeaf(RelationshipTypeCond.class).
                         filter(leaf -> AnyTypeKind.GROUP != svs.anyTypeKind).
                         ifPresent(leaf -> query.append(getQuery(leaf, not, parameters, svs)));
@@ -639,6 +643,30 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
         return query.toString();
     }
 
+    protected String getQuery(
+            final AuxClassCond cond,
+            final boolean not,
+            final List<Object> parameters,
+            final SearchSupport svs) {
+
+        StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM ").
+                append(svs.field().name).append(" WHERE ");
+
+        if (not) {
+            query.append("any_id NOT IN (");
+        } else {
+            query.append("any_id IN (");
+        }
+
+        query.append("SELECT DISTINCT any_id FROM ").
+                append(svs.auxClass().name).
+                append(" WHERE anyTypeClass_id=?").
+                append(setParameter(parameters, cond.getAuxClass())).
+                append(')');
+
+        return query.toString();
+    }
+
     protected String getQuery(
             final RelationshipTypeCond cond,
             final boolean not,
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java
index db48343ada..936315574d 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/SearchSupport.java
@@ -150,6 +150,10 @@ public class SearchSupport {
         return new SearchView("svdrealm", JPADynRealmDAO.DYNMEMB_TABLE);
     }
 
+    public SearchView auxClass() {
+        return new SearchView("svac", field().name + "_auxClass");
+    }
+
     public SearchView resource() {
         return new SearchView("svr", field().name + "_resource");
     }
diff --git a/core/persistence-jpa/src/main/resources/sqlserver_views.xml b/core/persistence-jpa/src/main/resources/sqlserver_views.xml
index 529901ece9..83dd7873a6 100644
--- a/core/persistence-jpa/src/main/resources/sqlserver_views.xml
+++ b/core/persistence-jpa/src/main/resources/sqlserver_views.xml
@@ -111,6 +111,12 @@ under the License.
     FROM DynRoleMembers drm, SyncopeRole_Privilege rp
     WHERE drm.role_id = rp.role_id
   </entry>
+  <entry key="user_search_auxClass">
+    CREATE VIEW user_search_auxClass AS
+
+    SELECT st.user_id AS any_id, st.anyTypeClass_id AS anyTypeClass_id
+    FROM SyncopeUser_AnyTypeClass st
+  </entry>
   <entry key="user_search_resource">
     CREATE VIEW user_search_resource AS
 
@@ -170,6 +176,12 @@ under the License.
     FROM AMembership m, SyncopeGroup g
     WHERE m.group_id = g.id
   </entry>
+  <entry key="anyObject_search_auxClass">
+    CREATE VIEW anyObject_search_auxClass AS
+
+    SELECT st.anyObject_id AS any_id, st.anyTypeClass_id AS anyTypeClass_id
+    FROM AnyObject_AnyTypeClass st
+  </entry>
   <entry key="anyObject_search_resource">
     CREATE VIEW anyObject_search_resource AS
 
@@ -216,6 +228,12 @@ under the License.
     FROM GPlainAttrValue uav, GPlainAttr ua
     WHERE uav.attribute_id = ua.id
   </entry>
+  <entry key="group_search_auxClass">
+    CREATE VIEW group_search_auxClass AS
+
+    SELECT st.group_id AS any_id, st.anyTypeClass_id AS anyTypeClass_id
+    FROM SyncopeGroup_AnyTypeClass st
+  </entry>
   <entry key="group_search_resource">
     CREATE VIEW group_search_resource AS
 
diff --git a/core/persistence-jpa/src/main/resources/views.xml b/core/persistence-jpa/src/main/resources/views.xml
index f1e8a603d6..e242f0ff92 100644
--- a/core/persistence-jpa/src/main/resources/views.xml
+++ b/core/persistence-jpa/src/main/resources/views.xml
@@ -111,6 +111,12 @@ under the License.
     FROM DynRoleMembers drm, SyncopeRole_Privilege rp
     WHERE drm.role_id = rp.role_id
   </entry>
+  <entry key="user_search_auxClass">
+    CREATE VIEW user_search_auxClass AS
+
+    SELECT st.user_id AS any_id, st.anyTypeClass_id AS anyTypeClass_id
+    FROM SyncopeUser_AnyTypeClass st
+  </entry>
   <entry key="user_search_resource">
     CREATE VIEW user_search_resource AS
 
@@ -170,6 +176,12 @@ under the License.
     FROM AMembership m, SyncopeGroup g
     WHERE m.group_id = g.id
   </entry>
+  <entry key="anyObject_search_auxClass">
+    CREATE VIEW anyObject_search_auxClass AS
+
+    SELECT st.anyObject_id AS any_id, st.anyTypeClass_id AS anyTypeClass_id
+    FROM AnyObject_AnyTypeClass st
+  </entry>
   <entry key="anyObject_search_resource">
     CREATE VIEW anyObject_search_resource AS
 
@@ -216,6 +228,12 @@ under the License.
     FROM GPlainAttrValue uav, GPlainAttr ua
     WHERE uav.attribute_id = ua.id
   </entry>
+  <entry key="group_search_auxClass">
+    CREATE VIEW group_search_auxClass AS
+
+    SELECT st.group_id AS any_id, st.anyTypeClass_id AS anyTypeClass_id
+    FROM SyncopeGroup_AnyTypeClass st
+  </entry>
   <entry key="group_search_resource">
     CREATE VIEW group_search_resource AS
 
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java
index e4dad5d2f4..926ce35b01 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java
@@ -52,6 +52,7 @@ import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
 import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
+import org.apache.syncope.core.persistence.api.dao.search.AuxClassCond;
 import org.apache.syncope.core.persistence.api.dao.search.MemberCond;
 import org.apache.syncope.core.persistence.api.dao.search.PrivilegeCond;
 import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond;
@@ -361,6 +362,16 @@ public class AnySearchTest extends AbstractTest {
         assertEquals(1, users.size());
     }
 
+    @Test
+    public void searchByAuxClass() {
+        AuxClassCond ac = new AuxClassCond();
+        ac.setAuxClass("csv");
+
+        List<Group> groups = searchDAO.search(SearchCond.getLeaf(ac), AnyTypeKind.GROUP);
+        assertNotNull(groups);
+        assertEquals(2, groups.size());
+    }
+
     @Test
     public void searchByResource() {
         ResourceCond ws2 = new ResourceCond();
diff --git a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
index 54d238519e..a95d09162c 100644
--- a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
+++ b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
@@ -32,6 +32,7 @@ import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
 import org.apache.syncope.core.persistence.api.entity.PlainAttr;
 import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
 import org.apache.syncope.core.persistence.api.entity.Privilege;
@@ -139,6 +140,7 @@ public class ElasticsearchUtils {
         builder.put("lastModifier", any.getLastModifier());
         builder.put("lastChangeContext", any.getLastChangeContext());
         builder.put("status", any.getStatus());
+        builder.put("auxClasses", any.getAuxClasses().stream().map(AnyTypeClass::getKey).collect(Collectors.toList()));
         builder.put("resources", resources);
         builder.put("dynRealms", dynRealms);
 
diff --git a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
index ad7dd141b3..7bc554eab0 100644
--- a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
+++ b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
@@ -58,6 +58,7 @@ import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
 import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond;
 import org.apache.syncope.core.persistence.api.dao.search.AssignableCond;
 import org.apache.syncope.core.persistence.api.dao.search.AttrCond;
+import org.apache.syncope.core.persistence.api.dao.search.AuxClassCond;
 import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond;
 import org.apache.syncope.core.persistence.api.dao.search.MemberCond;
 import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
@@ -371,6 +372,12 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
                             orElse(null);
                 }
 
+                if (query == null) {
+                    query = cond.getLeaf(AuxClassCond.class).
+                            map(this::getQuery).
+                            orElse(null);
+                }
+
                 if (query == null) {
                     query = cond.getLeaf(ResourceCond.class).
                             map(this::getQuery).
@@ -503,6 +510,12 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
                 build();
     }
 
+    protected Query getQuery(final AuxClassCond cond) {
+        return new Query.Builder().term(QueryBuilders.term().
+                field("auxClasses").value(FieldValue.of(cond.getAuxClass())).build()).
+                build();
+    }
+
     protected Query getQuery(final ResourceCond cond) {
         return new Query.Builder().term(QueryBuilders.term().
                 field("resources").value(FieldValue.of(cond.getResourceKey())).build()).
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java
index 6ccc7a7f68..8d9bcddfd8 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java
@@ -256,7 +256,23 @@ public class SearchITCase extends AbstractITCase {
     }
 
     @Test
-    public void searchUserByResourceName() {
+    public void searchByAuxClass() {
+        PagedResult<GroupTO> matchingGroups = groupService.search(
+                new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
+                        fiql(SyncopeClient.getGroupSearchConditionBuilder().
+                                hasAuxClasses("csv").query()).
+                        build());
+        assertNotNull(matchingGroups);
+        assertFalse(matchingGroups.getResult().isEmpty());
+
+        assertTrue(matchingGroups.getResult().stream().
+                anyMatch(group -> "0626100b-a4ba-4e00-9971-86fad52a6216".equals(group.getKey())));
+        assertTrue(matchingGroups.getResult().stream().
+                anyMatch(group -> "ba9ed509-b1f5-48ab-a334-c8530a6422dc".equals(group.getKey())));
+    }
+
+    @Test
+    public void searchByResource() {
         PagedResult<UserTO> matchingUsers = userService.search(
                 new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
                         fiql(SyncopeClient.getUserSearchConditionBuilder().
diff --git a/src/main/asciidoc/reference-guide/usage/core.adoc b/src/main/asciidoc/reference-guide/usage/core.adoc
index 3ae7fe1d9c..f4e85db687 100644
--- a/src/main/asciidoc/reference-guide/usage/core.adoc
+++ b/src/main/asciidoc/reference-guide/usage/core.adoc
@@ -454,6 +454,13 @@ lastLoginDate=ge=2016-03-02 15:21:22
 ----
 ====
 
+.Auxiliary Any Type class assignment
+====
+----
+$auxClasses==csv
+----
+====
+
 .Resource assignment match
 ====
 ----