syncope-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ilgro...@apache.org
Subject [2/4] syncope git commit: [SYNCOPE-152] Complete filter support, added sort support
Date Sun, 10 Dec 2017 14:07:47 GMT
[SYNCOPE-152] Complete filter support, added sort support


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/f2e8214e
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/f2e8214e
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/f2e8214e

Branch: refs/heads/master
Commit: f2e8214e9872d314703e4db4ba36342c3c4e0b42
Parents: 013c1de
Author: Francesco Chicchiriccò <ilgrosso@apache.org>
Authored: Sun Dec 10 14:33:19 2017 +0100
Committer: Francesco Chicchiriccò <ilgrosso@apache.org>
Committed: Sun Dec 10 15:02:53 2017 +0100

----------------------------------------------------------------------
 .../common/lib/scim/SCIMEnterpriseUserConf.java |  27 +++
 .../common/lib/scim/SCIMUserAddressConf.java    |  30 +++
 .../syncope/common/lib/scim/SCIMUserConf.java   |  36 ++++
 .../common/lib/scim/SCIMUserNameConf.java       |  30 +++
 .../syncope/core/logic/SCIMDataBinder.java      |  44 ++--
 .../core/logic/scim/SearchCondConverter.java    |   4 +-
 .../core/logic/scim/SearchCondVisitor.java      | 208 +++++++++++++++++--
 .../syncope/core/logic/scim/SCIMFilterTest.java | 134 ++++++++++--
 .../ext/scimv2/cxf/service/AbstractService.java |  40 +++-
 .../org/apache/syncope/fit/core/SCIMITCase.java |   8 +-
 10 files changed, 503 insertions(+), 58 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/f2e8214e/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMEnterpriseUserConf.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMEnterpriseUserConf.java
b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMEnterpriseUserConf.java
index 4cf7b88..e3c1441 100644
--- a/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMEnterpriseUserConf.java
+++ b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMEnterpriseUserConf.java
@@ -18,7 +18,11 @@
  */
 package org.apache.syncope.common.lib.scim;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 
 public class SCIMEnterpriseUserConf implements Serializable {
 
@@ -36,6 +40,29 @@ public class SCIMEnterpriseUserConf implements Serializable {
 
     private SCIMManagerConf manager;
 
+    @JsonIgnore
+    public Map<String, String> asMap() {
+        Map<String, String> map = new HashMap<>();
+
+        if (employeeNumber != null) {
+            map.put("employeeNumber", employeeNumber);
+        }
+        if (costCenter != null) {
+            map.put("costCenter", costCenter);
+        }
+        if (organization != null) {
+            map.put("organization", organization);
+        }
+        if (division != null) {
+            map.put("division", division);
+        }
+        if (department != null) {
+            map.put("department", department);
+        }
+
+        return Collections.unmodifiableMap(map);
+    }
+
     public String getEmployeeNumber() {
         return employeeNumber;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/f2e8214e/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMUserAddressConf.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMUserAddressConf.java
b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMUserAddressConf.java
index ed5643a..8c4e9ae 100644
--- a/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMUserAddressConf.java
+++ b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMUserAddressConf.java
@@ -18,7 +18,11 @@
  */
 package org.apache.syncope.common.lib.scim;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 import org.apache.syncope.common.lib.scim.types.AddressCanonicalType;
 
 public class SCIMUserAddressConf implements Serializable {
@@ -41,6 +45,32 @@ public class SCIMUserAddressConf implements Serializable {
 
     private boolean primary;
 
+    @JsonIgnore
+    public Map<String, String> asMap() {
+        Map<String, String> map = new HashMap<>();
+
+        if (formatted != null) {
+            map.put("formatted", formatted);
+        }
+        if (streetAddress != null) {
+            map.put("streetAddress", streetAddress);
+        }
+        if (locality != null) {
+            map.put("locality", locality);
+        }
+        if (region != null) {
+            map.put("region", region);
+        }
+        if (postalCode != null) {
+            map.put("postalCode", postalCode);
+        }
+        if (country != null) {
+            map.put("country", country);
+        }
+
+        return Collections.unmodifiableMap(map);
+    }
+
     public String getFormatted() {
         return formatted;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/f2e8214e/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMUserConf.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMUserConf.java
b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMUserConf.java
index 7d1be73..c2c0d15 100644
--- a/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMUserConf.java
+++ b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMUserConf.java
@@ -18,9 +18,13 @@
  */
 package org.apache.syncope.common.lib.scim;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import java.io.Serializable;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import org.apache.syncope.common.lib.scim.types.EmailCanonicalType;
 import org.apache.syncope.common.lib.scim.types.IMCanonicalType;
 import org.apache.syncope.common.lib.scim.types.PhoneNumberCanonicalType;
@@ -60,6 +64,38 @@ public class SCIMUserConf implements Serializable {
 
     private final List<String> x509Certificates = new ArrayList<>();
 
+    @JsonIgnore
+    public Map<String, String> asMap() {
+        Map<String, String> map = new HashMap<>();
+
+        if (displayName != null) {
+            map.put("displayName", displayName);
+        }
+        if (nickName != null) {
+            map.put("nickName", nickName);
+        }
+        if (profileUrl != null) {
+            map.put("profileUrl", profileUrl);
+        }
+        if (title != null) {
+            map.put("title", title);
+        }
+        if (userType != null) {
+            map.put("userType", userType);
+        }
+        if (preferredLanguage != null) {
+            map.put("preferredLanguage", preferredLanguage);
+        }
+        if (locale != null) {
+            map.put("locale", locale);
+        }
+        if (timezone != null) {
+            map.put("timezone", timezone);
+        }
+
+        return Collections.unmodifiableMap(map);
+    }
+
     public SCIMUserNameConf getName() {
         return name;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/f2e8214e/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMUserNameConf.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMUserNameConf.java
b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMUserNameConf.java
index 2f2db0f..2af8005 100644
--- a/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMUserNameConf.java
+++ b/ext/scimv2/common-lib/src/main/java/org/apache/syncope/common/lib/scim/SCIMUserNameConf.java
@@ -18,7 +18,11 @@
  */
 package org.apache.syncope.common.lib.scim;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import java.io.Serializable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 
 public class SCIMUserNameConf implements Serializable {
 
@@ -36,6 +40,32 @@ public class SCIMUserNameConf implements Serializable {
 
     private String honorificSuffix;
 
+    @JsonIgnore
+    public Map<String, String> asMap() {
+        Map<String, String> map = new HashMap<>();
+
+        if (formatted != null) {
+            map.put("formatted", formatted);
+        }
+        if (familyName != null) {
+            map.put("familyName", familyName);
+        }
+        if (givenName != null) {
+            map.put("givenName", givenName);
+        }
+        if (middleName != null) {
+            map.put("middleName", middleName);
+        }
+        if (honorificPrefix != null) {
+            map.put("honorificPrefix", honorificPrefix);
+        }
+        if (honorificSuffix != null) {
+            map.put("honorificSuffix", honorificSuffix);
+        }
+
+        return Collections.unmodifiableMap(map);
+    }
+
     public String getFormatted() {
         return formatted;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/f2e8214e/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMDataBinder.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMDataBinder.java
b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMDataBinder.java
index 4bc923a..03f78e6 100644
--- a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMDataBinder.java
+++ b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMDataBinder.java
@@ -431,32 +431,34 @@ public class SCIMDataBinder {
                         ? groupTO.getCreationDate() : groupTO.getLastChangeDate(),
                         groupTO.getETagValue(),
                         location),
-                output(attributes, excludedAttributes, "name", groupTO.getName()));
+                output(attributes, excludedAttributes, "displayName", groupTO.getName()));
 
         MembershipCond membCond = new MembershipCond();
         membCond.setGroup(groupTO.getKey());
         SearchCond searchCond = SearchCond.getLeafCond(membCond);
 
-        int count = userLogic.
-                search(searchCond, 1, 1, Collections.<OrderByClause>emptyList(), SyncopeConstants.ROOT_REALM,
false).
-                getLeft();
-
-        for (int page = 1; page <= (count / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
-            List<UserTO> users = userLogic.search(
-                    searchCond,
-                    page,
-                    AnyDAO.DEFAULT_PAGE_SIZE,
-                    Collections.<OrderByClause>emptyList(),
-                    SyncopeConstants.ROOT_REALM,
-                    false).
-                    getRight();
-            users.forEach(userTO -> {
-                group.getMembers().add(new Member(
-                        userTO.getKey(),
-                        StringUtils.substringBefore(location, "/Groups") + "/Users/" + userTO.getKey(),
-                        userTO.getUsername(),
-                        Resource.User));
-            });
+        if (output(attributes, excludedAttributes, "members")) {
+            int count = userLogic.search(searchCond,
+                    1, 1, Collections.<OrderByClause>emptyList(),
+                    SyncopeConstants.ROOT_REALM, false).getLeft();
+
+            for (int page = 1; page <= (count / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++)
{
+                List<UserTO> users = userLogic.search(
+                        searchCond,
+                        page,
+                        AnyDAO.DEFAULT_PAGE_SIZE,
+                        Collections.<OrderByClause>emptyList(),
+                        SyncopeConstants.ROOT_REALM,
+                        false).
+                        getRight();
+                users.forEach(userTO -> {
+                    group.getMembers().add(new Member(
+                            userTO.getKey(),
+                            StringUtils.substringBefore(location, "/Groups") + "/Users/"
+ userTO.getKey(),
+                            userTO.getUsername(),
+                            Resource.User));
+                });
+            }
         }
 
         return group;

http://git-wip-us.apache.org/repos/asf/syncope/blob/f2e8214e/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SearchCondConverter.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SearchCondConverter.java
b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SearchCondConverter.java
index fd45f43..9ad7b82 100644
--- a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SearchCondConverter.java
+++ b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SearchCondConverter.java
@@ -33,7 +33,7 @@ public final class SearchCondConverter {
 
     private static final Logger LOG = LoggerFactory.getLogger(SearchCondConverter.class);
 
-    public static SearchCond convert(final String filter) {
+    public static SearchCond convert(final SearchCondVisitor visitor, final String filter)
{
         SCIMFilterParser parser = new SCIMFilterParser(new CommonTokenStream(
                 new SCIMFilterLexer(CharStreams.fromString(filter))));
         parser.setBuildParseTree(true);
@@ -43,7 +43,7 @@ public final class SearchCondConverter {
         parser.setErrorHandler(new SCIMFilterErrorHandler());
 
         try {
-            return new SearchCondVisitor().visit(parser.scimFilter());
+            return visitor.visit(parser.scimFilter());
         } catch (Exception e) {
             LOG.error("Could not parse {}", filter, e);
             throw new SCIMBadRequestException(ErrorType.invalidFilter, e.getMessage());

http://git-wip-us.apache.org/repos/asf/syncope/blob/f2e8214e/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SearchCondVisitor.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SearchCondVisitor.java
b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SearchCondVisitor.java
index 1882505..6d07d30 100644
--- a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SearchCondVisitor.java
+++ b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SearchCondVisitor.java
@@ -18,7 +18,16 @@
  */
 package org.apache.syncope.core.logic.scim;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.scim.SCIMComplexConf;
+import org.apache.syncope.common.lib.scim.SCIMConf;
+import org.apache.syncope.common.lib.scim.SCIMUserAddressConf;
+import org.apache.syncope.common.lib.scim.SCIMUserConf;
 import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
 import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
@@ -29,41 +38,101 @@ import org.apache.syncope.ext.scimv2.api.type.Resource;
  */
 public class SearchCondVisitor extends SCIMFilterBaseVisitor<SearchCond> {
 
+    private static final List<String> MULTIVALUE = Arrays.asList(
+            "emails", "phoneNumbers", "ims", "photos", "addresses");
+
+    private final Resource resource;
+
+    private final SCIMConf conf;
+
+    public SearchCondVisitor(final Resource resource, final SCIMConf conf) {
+        this.resource = resource;
+        this.conf = conf;
+    }
+
     @Override
     public SearchCond visitScimFilter(final SCIMFilterParser.ScimFilterContext ctx) {
         return visit(ctx.expression(0));
     }
 
-    private AttributeCond createAttributeCond(final String schema) {
-        AttributeCond attributeCond;
-        if ("userName".equalsIgnoreCase(schema)
-                || (Resource.User.schema() + ":userName").equalsIgnoreCase(schema)) {
+    private boolean schemaEquals(final Resource resource, final String value, final String
schema) {
+        return resource == null
+                ? value.contains(":")
+                ? StringUtils.substringAfterLast(value, ":").equalsIgnoreCase(schema)
+                : value.equalsIgnoreCase(schema)
+                : value.equalsIgnoreCase(schema) || (resource.schema() + ":" + value).equalsIgnoreCase(schema);
+    }
+
+    public AttributeCond createAttributeCond(final String schema) {
+        AttributeCond attributeCond = null;
 
+        if (schemaEquals(Resource.User, "userName", schema)) {
             attributeCond = new AnyCond();
             attributeCond.setSchema("username");
-        } else if ("displayName".equalsIgnoreCase(schema)
-                || (Resource.Group.schema() + ":displayName").equalsIgnoreCase(schema)) {
-
+        } else if (resource == Resource.Group && schemaEquals(Resource.Group, "displayName",
schema)) {
             attributeCond = new AnyCond();
             attributeCond.setSchema("name");
-        } else if ("meta.created".equals(schema)) {
+        } else if (schemaEquals(null, "meta.created", schema)) {
             attributeCond = new AnyCond();
             attributeCond.setSchema("creationDate");
-        } else if ("meta.lastModified".equals(schema)) {
+        } else if (schemaEquals(null, "meta.lastModified", schema)) {
             attributeCond = new AnyCond();
             attributeCond.setSchema("lastChangeDate");
-        } else {
-            attributeCond = new AttributeCond();
-            attributeCond.setSchema(schema);
+        }
+
+        if (resource == Resource.User) {
+            if (conf.getUserConf() != null) {
+                if (conf.getUserConf().getName() != null) {
+                    for (Map.Entry<String, String> entry : conf.getUserConf().getName().asMap().entrySet())
{
+                        if (schemaEquals(Resource.User, "name." + entry.getKey(), schema))
{
+                            attributeCond = new AttributeCond();
+                            attributeCond.setSchema(entry.getValue());
+                        }
+                    }
+                }
+
+                for (Map.Entry<String, String> entry : conf.getUserConf().asMap().entrySet())
{
+                    if (schemaEquals(Resource.User, entry.getKey(), schema)) {
+                        attributeCond = new AttributeCond();
+                        attributeCond.setSchema(entry.getValue());
+                    }
+                }
+
+                for (SCIMUserAddressConf address : conf.getUserConf().getAddresses()) {
+                    for (Map.Entry<String, String> entry : address.asMap().entrySet())
{
+                        if (schemaEquals(Resource.User, "addresses." + entry.getKey(), schema))
{
+                            attributeCond = new AttributeCond();
+                            attributeCond.setSchema(entry.getValue());
+                        }
+                    }
+                }
+            }
+
+            if (conf.getEnterpriseUserConf() != null) {
+                for (Map.Entry<String, String> entry : conf.getEnterpriseUserConf().asMap().entrySet())
{
+                    if (schemaEquals(Resource.EnterpriseUser, entry.getKey(), schema)) {
+                        attributeCond = new AttributeCond();
+                        attributeCond.setSchema(entry.getValue());
+                    }
+                }
+
+                if (conf.getEnterpriseUserConf().getManager() != null
+                        && conf.getEnterpriseUserConf().getManager().getManager()
!= null) {
+
+                    attributeCond = new AttributeCond();
+                    attributeCond.setSchema(conf.getEnterpriseUserConf().getManager().getManager());
+                }
+            }
+        }
+
+        if (attributeCond == null) {
+            throw new IllegalArgumentException("Could not match " + schema + " for " + resource);
         }
 
         return attributeCond;
     }
 
-    private SearchCond transform(final String operator, final String left, final String right)
{
-        AttributeCond attributeCond = createAttributeCond(left);
-        attributeCond.setExpression(StringUtils.strip(right, "\""));
-
+    private SearchCond setOperator(final AttributeCond attributeCond, final String operator)
{
         switch (operator) {
             case "eq":
             default:
@@ -104,7 +173,6 @@ public class SearchCondVisitor extends SCIMFilterBaseVisitor<SearchCond>
{
             case "le":
                 attributeCond.setType(AttributeCond.Type.LE);
                 break;
-
         }
 
         return "ne".equals(operator)
@@ -112,6 +180,112 @@ public class SearchCondVisitor extends SCIMFilterBaseVisitor<SearchCond>
{
                 : SearchCond.getLeafCond(attributeCond);
     }
 
+    private <E extends Enum<?>> SearchCond complex(
+            final String operator, final String left, final String right, final List<SCIMComplexConf<E>>
items) {
+
+        if (left.endsWith(".type")) {
+            Optional<SCIMComplexConf<E>> item = items.stream().
+                    filter(object -> object.getType().name().equals(StringUtils.strip(right,
"\""))).findFirst();
+            if (item.isPresent()) {
+                AttributeCond attributeCond = new AttributeCond();
+                attributeCond.setSchema(item.get().getValue());
+                attributeCond.setType(AttributeCond.Type.ISNOTNULL);
+                return SearchCond.getLeafCond(attributeCond);
+            }
+        } else if (!conf.getUserConf().getEmails().isEmpty()
+                && (MULTIVALUE.contains(left) || left.endsWith(".value"))) {
+
+            List<SearchCond> orConds = new ArrayList<>();
+            items.forEach(item -> {
+                AttributeCond cond = new AttributeCond();
+                cond.setSchema(item.getValue());
+                cond.setExpression(StringUtils.strip(right, "\""));
+                orConds.add(setOperator(cond, operator));
+            });
+            if (!orConds.isEmpty()) {
+                return SearchCond.getOrCond(orConds);
+            }
+        }
+
+        return null;
+    }
+
+    private SearchCond addresses(
+            final String operator, final String left, final String right, final List<SCIMUserAddressConf>
items) {
+
+        if (left.endsWith(".type") && "eq".equals(operator)) {
+            Optional<SCIMUserAddressConf> item = items.stream().
+                    filter(object -> object.getType().name().equals(StringUtils.strip(right,
"\""))).findFirst();
+            if (item.isPresent()) {
+                AttributeCond attributeCond = new AttributeCond();
+                attributeCond.setSchema(item.get().getFormatted());
+                attributeCond.setType(AttributeCond.Type.ISNOTNULL);
+                return SearchCond.getLeafCond(attributeCond);
+            }
+        } else if (!conf.getUserConf().getEmails().isEmpty()
+                && (MULTIVALUE.contains(left) || left.endsWith(".value"))) {
+
+            List<SearchCond> orConds = new ArrayList<>();
+            items.forEach(item -> {
+                AttributeCond cond = new AttributeCond();
+                cond.setSchema(item.getFormatted());
+                cond.setExpression(StringUtils.strip(right, "\""));
+                orConds.add(setOperator(cond, operator));
+            });
+            if (!orConds.isEmpty()) {
+                return SearchCond.getOrCond(orConds);
+            }
+        }
+
+        return null;
+    }
+
+    private SearchCond transform(final String operator, final String left, final String right)
{
+        SearchCond result = null;
+
+        if (MULTIVALUE.contains(StringUtils.substringBefore(left, "."))) {
+            if (conf.getUserConf() == null) {
+                throw new IllegalArgumentException("No " + SCIMUserConf.class.getName() +
" provided, cannot continue");
+            }
+
+            switch (StringUtils.substringBefore(left, ".")) {
+                case "emails":
+                    result = complex(operator, left, right, conf.getUserConf().getEmails());
+                    break;
+
+                case "phoneNumbers":
+                    result = complex(operator, left, right, conf.getUserConf().getPhoneNumbers());
+                    break;
+
+                case "ims":
+                    result = complex(operator, left, right, conf.getUserConf().getIms());
+                    break;
+
+                case "photos":
+                    result = complex(operator, left, right, conf.getUserConf().getPhotos());
+                    break;
+
+                case "addresses":
+                    result = addresses(operator, left, right, conf.getUserConf().getAddresses());
+                    break;
+
+                default:
+            }
+        }
+
+        if (result == null) {
+            AttributeCond attributeCond = createAttributeCond(left);
+            attributeCond.setExpression(StringUtils.strip(right, "\""));
+            result = setOperator(attributeCond, operator);
+        }
+
+        if (result == null) {
+            throw new IllegalArgumentException(
+                    "Could not handle (" + left + " " + operator + " " + right + ") for "
+ resource);
+        }
+        return result;
+    }
+
     @Override
     public SearchCond visitEXPR_OPER_EXPR(final SCIMFilterParser.EXPR_OPER_EXPRContext ctx)
{
         return transform(ctx.operator().getText(), ctx.expression(0).getText(), ctx.expression(1).getText());

http://git-wip-us.apache.org/repos/asf/syncope/blob/f2e8214e/ext/scimv2/logic/src/test/java/org/apache/syncope/core/logic/scim/SCIMFilterTest.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/logic/src/test/java/org/apache/syncope/core/logic/scim/SCIMFilterTest.java
b/ext/scimv2/logic/src/test/java/org/apache/syncope/core/logic/scim/SCIMFilterTest.java
index d488725..1082869 100644
--- a/ext/scimv2/logic/src/test/java/org/apache/syncope/core/logic/scim/SCIMFilterTest.java
+++ b/ext/scimv2/logic/src/test/java/org/apache/syncope/core/logic/scim/SCIMFilterTest.java
@@ -22,15 +22,47 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 
+import org.apache.syncope.common.lib.scim.SCIMComplexConf;
+import org.apache.syncope.common.lib.scim.SCIMConf;
+import org.apache.syncope.common.lib.scim.SCIMUserConf;
+import org.apache.syncope.common.lib.scim.SCIMUserNameConf;
+import org.apache.syncope.common.lib.scim.types.EmailCanonicalType;
 import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.ext.scimv2.api.type.Resource;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
 public class SCIMFilterTest {
 
+    private static SearchCondVisitor VISITOR;
+
+    @BeforeAll
+    public static void setup() {
+        SCIMConf conf = new SCIMConf();
+        conf.setUserConf(new SCIMUserConf());
+        conf.getUserConf().setTitle("title");
+        conf.getUserConf().setDisplayName("cn");
+        conf.getUserConf().setUserType("userType");
+
+        conf.getUserConf().setName(new SCIMUserNameConf());
+        conf.getUserConf().getName().setFamilyName("surname");
+
+        SCIMComplexConf<EmailCanonicalType> email = new SCIMComplexConf<>();
+        email.setValue("email");
+        email.setType(EmailCanonicalType.work);
+        conf.getUserConf().getEmails().add(email);
+        email = new SCIMComplexConf<>();
+        email.setValue("gmail");
+        email.setType(EmailCanonicalType.home);
+        conf.getUserConf().getEmails().add(email);
+
+        VISITOR = new SearchCondVisitor(Resource.User, conf);
+    }
+
     @Test
     public void eq() {
-        SearchCond cond = SearchCondConverter.convert("userName eq \"bjensen\"");
+        SearchCond cond = SearchCondConverter.convert(VISITOR, "userName eq \"bjensen\"");
         assertNotNull(cond);
         assertNotNull(cond.getAnyCond());
         assertEquals("username", cond.getAnyCond().getSchema());
@@ -40,20 +72,21 @@ public class SCIMFilterTest {
 
     @Test
     public void sw() {
-        SearchCond cond = SearchCondConverter.convert("userName sw \"J\"");
+        SearchCond cond = SearchCondConverter.convert(VISITOR, "userName sw \"J\"");
         assertNotNull(cond);
         assertNotNull(cond.getAnyCond());
         assertEquals("username", cond.getAnyCond().getSchema());
         assertEquals(AttributeCond.Type.ILIKE, cond.getAnyCond().getType());
         assertEquals("J%", cond.getAnyCond().getExpression());
 
-        SearchCond fqn = SearchCondConverter.convert("urn:ietf:params:scim:schemas:core:2.0:User:userName
sw \"J\"");
+        SearchCond fqn = SearchCondConverter.convert(
+                VISITOR, "urn:ietf:params:scim:schemas:core:2.0:User:userName sw \"J\"");
         assertEquals(cond, fqn);
     }
 
     @Test
     public void pr() {
-        SearchCond cond = SearchCondConverter.convert("title pr");
+        SearchCond cond = SearchCondConverter.convert(VISITOR, "title pr");
         assertNotNull(cond);
         assertNotNull(cond.getAttributeCond());
         assertEquals("title", cond.getAttributeCond().getSchema());
@@ -63,7 +96,7 @@ public class SCIMFilterTest {
 
     @Test
     public void gt() {
-        SearchCond cond = SearchCondConverter.convert("meta.lastModified gt \"2011-05-13T04:42:34Z\"");
+        SearchCond cond = SearchCondConverter.convert(VISITOR, "meta.lastModified gt \"2011-05-13T04:42:34Z\"");
         assertNotNull(cond);
         assertNotNull(cond.getAnyCond());
         assertEquals("lastChangeDate", cond.getAnyCond().getSchema());
@@ -73,7 +106,7 @@ public class SCIMFilterTest {
 
     @Test
     public void not() {
-        SearchCond cond = SearchCondConverter.convert("not (title pr)");
+        SearchCond cond = SearchCondConverter.convert(VISITOR, "not (title pr)");
         assertNotNull(cond);
         assertNotNull(cond.getAttributeCond());
         assertEquals("title", cond.getAttributeCond().getSchema());
@@ -83,7 +116,7 @@ public class SCIMFilterTest {
 
     @Test
     public void and() {
-        SearchCond cond = SearchCondConverter.convert("title pr and userName sw \"J\"");
+        SearchCond cond = SearchCondConverter.convert(VISITOR, "title pr and userName sw
\"J\"");
         assertNotNull(cond);
         assertEquals(SearchCond.Type.AND, cond.getType());
 
@@ -104,7 +137,7 @@ public class SCIMFilterTest {
 
     @Test
     public void or() {
-        SearchCond cond = SearchCondConverter.convert("title pr or displayName eq \"Other\"");
+        SearchCond cond = SearchCondConverter.convert(VISITOR, "title pr or displayName eq
\"Other\"");
         assertNotNull(cond);
         assertEquals(SearchCond.Type.OR, cond.getType());
 
@@ -117,9 +150,86 @@ public class SCIMFilterTest {
 
         SearchCond right = cond.getRightSearchCond();
         assertNotNull(right);
-        assertNotNull(right.getAnyCond());
-        assertEquals("name", right.getAnyCond().getSchema());
-        assertEquals(AttributeCond.Type.IEQ, right.getAnyCond().getType());
-        assertEquals("Other", right.getAnyCond().getExpression());
+        assertNotNull(right.getAttributeCond());
+        assertEquals("cn", right.getAttributeCond().getSchema());
+        assertEquals(AttributeCond.Type.IEQ, right.getAttributeCond().getType());
+        assertEquals("Other", right.getAttributeCond().getExpression());
+    }
+
+    @Test
+    public void type() {
+        SearchCond cond = SearchCondConverter.convert(VISITOR, "userType eq \"Employee\"
and (emails.type eq \"work\")");
+        assertNotNull(cond);
+        assertEquals(SearchCond.Type.AND, cond.getType());
+
+        SearchCond left = cond.getLeftSearchCond();
+        assertNotNull(left);
+        assertNotNull(left.getAttributeCond());
+        assertEquals("userType", left.getAttributeCond().getSchema());
+        assertEquals(AttributeCond.Type.IEQ, left.getAttributeCond().getType());
+        assertEquals("Employee", left.getAttributeCond().getExpression());
+
+        SearchCond right = cond.getRightSearchCond();
+        assertNotNull(right);
+        assertNotNull(right.getAttributeCond());
+        assertEquals("email", right.getAttributeCond().getSchema());
+        assertEquals(AttributeCond.Type.ISNOTNULL, right.getAttributeCond().getType());
+    }
+
+    @Test
+    public void name() {
+        SearchCond cond = SearchCondConverter.convert(VISITOR, "name.familyName co \"O'Malley\"");
+        assertNotNull(cond);
+        assertEquals(SearchCond.Type.LEAF, cond.getType());
+
+        AttributeCond leaf = cond.getAttributeCond();
+        assertNotNull(leaf);
+        assertEquals("surname", leaf.getSchema());
+        assertEquals(AttributeCond.Type.ILIKE, leaf.getType());
+        assertEquals("%O'Malley%", leaf.getExpression());
+    }
+
+    @Test
+    public void emails() {
+        SearchCond cond = SearchCondConverter.convert(VISITOR,
+                "emails co \"example.com\" or emails.value co \"example.org\"");
+        assertNotNull(cond);
+        assertEquals(SearchCond.Type.OR, cond.getType());
+
+        SearchCond left = cond.getLeftSearchCond();
+        assertNotNull(left);
+        assertEquals(SearchCond.Type.OR, left.getType());
+
+        SearchCond left1 = left.getLeftSearchCond();
+        assertNotNull(left1);
+        assertNotNull(left1.getAttributeCond());
+        assertEquals("email", left1.getAttributeCond().getSchema());
+        assertEquals(AttributeCond.Type.ILIKE, left1.getAttributeCond().getType());
+        assertEquals("%example.com%", left1.getAttributeCond().getExpression());
+
+        SearchCond left2 = left.getRightSearchCond();
+        assertNotNull(left2);
+        assertNotNull(left2.getAttributeCond());
+        assertEquals("gmail", left2.getAttributeCond().getSchema());
+        assertEquals(AttributeCond.Type.ILIKE, left2.getAttributeCond().getType());
+        assertEquals("%example.com%", left2.getAttributeCond().getExpression());
+
+        SearchCond right = cond.getRightSearchCond();
+        assertNotNull(right);
+        assertEquals(SearchCond.Type.OR, right.getType());
+
+        SearchCond right1 = right.getLeftSearchCond();
+        assertNotNull(right1);
+        assertNotNull(right1.getAttributeCond());
+        assertEquals("email", right1.getAttributeCond().getSchema());
+        assertEquals(AttributeCond.Type.ILIKE, right1.getAttributeCond().getType());
+        assertEquals("%example.org%", right1.getAttributeCond().getExpression());
+
+        SearchCond right2 = right.getRightSearchCond();
+        assertNotNull(right2);
+        assertNotNull(right2.getAttributeCond());
+        assertEquals("gmail", right2.getAttributeCond().getSchema());
+        assertEquals(AttributeCond.Type.ILIKE, right2.getAttributeCond().getType());
+        assertEquals("%example.org%", right2.getAttributeCond().getExpression());
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/f2e8214e/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractService.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractService.java
b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractService.java
index a28209a..1092c0a 100644
--- a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractService.java
+++ b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractService.java
@@ -33,7 +33,9 @@ import org.apache.syncope.core.logic.AbstractAnyLogic;
 import org.apache.syncope.core.logic.GroupLogic;
 import org.apache.syncope.core.logic.SCIMDataBinder;
 import org.apache.syncope.core.logic.UserLogic;
+import org.apache.syncope.core.logic.scim.SCIMConfManager;
 import org.apache.syncope.core.logic.scim.SearchCondConverter;
+import org.apache.syncope.core.logic.scim.SearchCondVisitor;
 import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
@@ -41,6 +43,7 @@ import org.apache.syncope.ext.scimv2.api.data.ListResponse;
 import org.apache.syncope.ext.scimv2.api.data.SCIMResource;
 import org.apache.syncope.ext.scimv2.api.data.SCIMSearchRequest;
 import org.apache.syncope.ext.scimv2.api.type.Resource;
+import org.apache.syncope.ext.scimv2.api.type.SortOrder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -60,6 +63,8 @@ abstract class AbstractService<R extends SCIMResource> {
 
     private SCIMDataBinder binder;
 
+    private SCIMConfManager confManager;
+
     protected UserLogic userLogic() {
         synchronized (this) {
             if (userLogic == null) {
@@ -100,6 +105,15 @@ abstract class AbstractService<R extends SCIMResource> {
         }
     }
 
+    protected SCIMConfManager confManager() {
+        synchronized (this) {
+            if (confManager == null) {
+                confManager = ApplicationContextProvider.getApplicationContext().getBean(SCIMConfManager.class);
+            }
+        }
+        return confManager;
+    }
+
     @SuppressWarnings("unchecked")
     protected ListResponse<R> doSearch(
             final Resource type,
@@ -109,20 +123,38 @@ abstract class AbstractService<R extends SCIMResource> {
             throw new UnsupportedOperationException();
         }
 
+        SearchCondVisitor visitor = new SearchCondVisitor(type, confManager().get());
+
         int startIndex = request.getStartIndex() == null || request.getStartIndex() <=
1
                 ? 1
                 : (request.getStartIndex() / AnyDAO.DEFAULT_PAGE_SIZE) + 1;
 
+        int itemsPerPage = request.getCount() == null ? AnyDAO.DEFAULT_PAGE_SIZE : request.getCount();
+
+        List<OrderByClause> sort;
+        if (request.getSortBy() == null) {
+            sort = Collections.<OrderByClause>emptyList();
+        } else {
+            OrderByClause clause = new OrderByClause();
+            clause.setField(visitor.createAttributeCond(request.getSortBy()).getSchema());
+            clause.setDirection(request.getSortOrder() == null || request.getSortOrder()
== SortOrder.ascending
+                    ? OrderByClause.Direction.ASC
+                    : OrderByClause.Direction.DESC);
+            sort = Collections.singletonList(clause);
+        }
+
         Pair<Integer, ? extends List<? extends AnyTO>> result = anyLogic(type).search(
-                StringUtils.isBlank(request.getFilter()) ? null : SearchCondConverter.convert(request.getFilter()),
+                StringUtils.isBlank(request.getFilter())
+                ? null
+                : SearchCondConverter.convert(visitor, request.getFilter()),
                 startIndex,
-                AnyDAO.DEFAULT_PAGE_SIZE,
-                Collections.<OrderByClause>emptyList(),
+                itemsPerPage,
+                sort,
                 SyncopeConstants.ROOT_REALM,
                 false);
 
         ListResponse<R> response = new ListResponse<>(
-                result.getLeft(), startIndex == 1 ? 1 : startIndex - 1, AnyDAO.DEFAULT_PAGE_SIZE);
+                result.getLeft(), startIndex == 1 ? 1 : startIndex - 1, itemsPerPage);
 
         result.getRight().forEach(anyTO -> {
             SCIMResource resource = null;

http://git-wip-us.apache.org/repos/asf/syncope/blob/f2e8214e/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SCIMITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SCIMITCase.java
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SCIMITCase.java
index c44a847..ba4613f 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SCIMITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SCIMITCase.java
@@ -204,7 +204,10 @@ public class SCIMITCase extends AbstractITCase {
     public void list() throws IOException {
         assumeTrue(SCIMDetector.isSCIMAvailable(webClient()));
 
-        Response response = webClient().path("Groups").get();
+        Response response = webClient().path("Groups").
+                query("sortBy", "displayName").
+                query("count", 11).
+                get();
         assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
         assertEquals(
                 SCIMConstants.APPLICATION_SCIM_JSON,
@@ -214,8 +217,9 @@ public class SCIMITCase extends AbstractITCase {
         });
         assertNotNull(result);
         assertTrue(result.getTotalResults() > 0);
-        assertFalse(result.getResources().isEmpty());
+        assertEquals(11, result.getItemsPerPage());
 
+        assertFalse(result.getResources().isEmpty());
         result.getResources().forEach(group -> {
             assertNotNull(group.getId());
             assertNotNull(group.getDisplayName());


Mime
View raw message