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 2017/11/06 12:57:20 UTC

[1/6] syncope git commit: More robust ConnObjectKey handling

Repository: syncope
Updated Branches:
  refs/heads/2_0_X 149b91000 -> 1c469ae17
  refs/heads/master 3d4f233cc -> 4989ffd04


More robust ConnObjectKey handling


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

Branch: refs/heads/2_0_X
Commit: b978fbecc404f992ea03b6eedfe344d68bdef164
Parents: 149b910
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Mon Nov 6 09:08:33 2017 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Mon Nov 6 09:08:33 2017 +0100

----------------------------------------------------------------------
 .../provisioning/java/MappingManagerImpl.java   | 24 ++++++++++----------
 1 file changed, 12 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/b978fbec/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
index 40ebcd1..d640187 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
@@ -191,18 +191,18 @@ public class MappingManagerImpl implements MappingManager {
             }
         }
 
-        Attribute connObjectKeyExtAttr =
-                AttributeUtil.find(MappingUtils.getConnObjectKeyItem(provision).getExtAttrName(), attributes);
-        if (connObjectKeyExtAttr != null) {
-            attributes.remove(connObjectKeyExtAttr);
-            attributes.add(AttributeBuilder.build(
-                    MappingUtils.getConnObjectKeyItem(provision).getExtAttrName(), connObjectKey));
-        }
-        Name name = MappingUtils.evaluateNAME(any, provision, connObjectKey);
-        attributes.add(name);
-        if (connObjectKey != null && !connObjectKey.equals(name.getNameValue()) && connObjectKeyExtAttr == null) {
-            attributes.add(AttributeBuilder.build(
-                    MappingUtils.getConnObjectKeyItem(provision).getExtAttrName(), connObjectKey));
+        MappingItem connObjectKeyItem = MappingUtils.getConnObjectKeyItem(provision);
+        if (connObjectKeyItem != null) {
+            Attribute connObjectKeyExtAttr = AttributeUtil.find(connObjectKeyItem.getExtAttrName(), attributes);
+            if (connObjectKeyExtAttr != null) {
+                attributes.remove(connObjectKeyExtAttr);
+                attributes.add(AttributeBuilder.build(connObjectKeyItem.getExtAttrName(), connObjectKey));
+            }
+            Name name = MappingUtils.evaluateNAME(any, provision, connObjectKey);
+            attributes.add(name);
+            if (connObjectKey != null && !connObjectKey.equals(name.getNameValue()) && connObjectKeyExtAttr == null) {
+                attributes.add(AttributeBuilder.build(connObjectKeyItem.getExtAttrName(), connObjectKey));
+            }
         }
 
         if (enable != null) {


[2/6] syncope git commit: White noise: javadocs cleanup

Posted by il...@apache.org.
White noise: javadocs cleanup


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

Branch: refs/heads/2_0_X
Commit: 99a9f1a7fc1ff5e5bccea76536741d17db523744
Parents: b978fbe
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Mon Nov 6 12:50:43 2017 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Mon Nov 6 12:50:43 2017 +0100

----------------------------------------------------------------------
 .../syncope/core/persistence/api/search/SearchCondConverter.java  | 3 +--
 .../syncope/core/persistence/api/search/SearchCondVisitor.java    | 2 +-
 2 files changed, 2 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/99a9f1a7/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondConverter.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondConverter.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondConverter.java
index 1ec0f6f..257ba7f 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondConverter.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondConverter.java
@@ -35,12 +35,11 @@ import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 public final class SearchCondConverter {
 
     /**
-     * Parses a FIQL expression into Syncope's <tt>SearchCond</tt>, using CXF's <tt>FiqlParser</tt>.
+     * Parses a FIQL expression into Syncope's {@link SearchCond}, using {@link SyncopeFiqlParser}.
      *
      * @param fiql FIQL string
      * @param realms optional realm to provide to {@link SearchCondVisitor}
      * @return {@link SearchCond} instance for given FIQL expression
-     * @see SyncopeFiqlParser
      */
     public static SearchCond convert(final String fiql, final String... realms) {
         SyncopeFiqlParser<SearchBean> parser = new SyncopeFiqlParser<>(

http://git-wip-us.apache.org/repos/asf/syncope/blob/99a9f1a7/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java
----------------------------------------------------------------------
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 f81adc2..e9e5951 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
@@ -46,7 +46,7 @@ import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond;
 import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond;
 
 /**
- * Converts CXF's <tt>SearchCondition</tt> into internal <tt>SearchCond</tt>.
+ * Visits CXF's {@link SearchBean} and produces {@link SearchCond}.
  */
 public class SearchCondVisitor extends AbstractSearchConditionVisitor<SearchBean, SearchCond> {
 


[6/6] syncope git commit: [SYNCOPE-152] SCIM filter

Posted by il...@apache.org.
[SYNCOPE-152] SCIM filter


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

Branch: refs/heads/master
Commit: 4989ffd040f82cd76fba0d52b61cb9b8bd405ee5
Parents: 9129ee0
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Mon Nov 6 13:50:09 2017 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Mon Nov 6 13:57:02 2017 +0100

----------------------------------------------------------------------
 ext/scimv2/logic/pom.xml                        |  27 +++
 .../syncope/core/logic/scim/SCIMFilter.g4       |  77 ++++++++
 .../core/logic/scim/SCIMFilterErrorHandler.java |  42 +++++
 .../core/logic/scim/SearchCondConverter.java    |  56 ++++++
 .../core/logic/scim/SearchCondVisitor.java      | 174 +++++++++++++++++++
 .../syncope/core/logic/scim/SCIMFilterTest.java | 125 +++++++++++++
 .../syncope/ext/scimv2/api/data/Group.java      |   2 +-
 .../syncope/ext/scimv2/api/data/Member.java     |   2 +-
 .../syncope/ext/scimv2/api/data/SCIMError.java  |   5 +
 .../ext/scimv2/cxf/SCIMExceptionMapper.java     |  10 +-
 .../scimv2/cxf/service/AbstractSCIMService.java |   7 +-
 .../org/apache/syncope/fit/core/SCIMITCase.java |  56 ++++++
 pom.xml                                         |  14 ++
 13 files changed, 587 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/4989ffd0/ext/scimv2/logic/pom.xml
----------------------------------------------------------------------
diff --git a/ext/scimv2/logic/pom.xml b/ext/scimv2/logic/pom.xml
index abc90b1..4569445 100644
--- a/ext/scimv2/logic/pom.xml
+++ b/ext/scimv2/logic/pom.xml
@@ -35,6 +35,7 @@ under the License.
   
   <properties>
     <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+    <antlr4.visitor>true</antlr4.visitor>
   </properties>
 
   <dependencies>
@@ -49,6 +50,17 @@ under the License.
       <artifactId>syncope-ext-scimv2-scim-rest-api</artifactId>
       <version>${project.version}</version>
     </dependency>
+    
+    <dependency>
+      <groupId>org.antlr</groupId>
+      <artifactId>antlr4-runtime</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-engine</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
@@ -56,6 +68,21 @@ under the License.
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-checkstyle-plugin</artifactId>
+        <configuration>
+          <sourceDirectory>${project.build.sourceDirectory}</sourceDirectory>
+        </configuration>
+      </plugin>
+      
+      <plugin>
+        <groupId>org.antlr</groupId>
+        <artifactId>antlr4-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>antlr4</goal>
+            </goals>
+          </execution>
+        </executions>
       </plugin>
     </plugins>
   </build>

http://git-wip-us.apache.org/repos/asf/syncope/blob/4989ffd0/ext/scimv2/logic/src/main/antlr4/org/apache/syncope/core/logic/scim/SCIMFilter.g4
----------------------------------------------------------------------
diff --git a/ext/scimv2/logic/src/main/antlr4/org/apache/syncope/core/logic/scim/SCIMFilter.g4 b/ext/scimv2/logic/src/main/antlr4/org/apache/syncope/core/logic/scim/SCIMFilter.g4
new file mode 100644
index 0000000..ffddf32
--- /dev/null
+++ b/ext/scimv2/logic/src/main/antlr4/org/apache/syncope/core/logic/scim/SCIMFilter.g4
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+grammar SCIMFilter;
+
+options
+{
+  language = Java;
+}
+
+scimFilter
+ : expression* EOF
+ ;
+
+expression
+ : NOT WS+? expression                        # NOT_EXPR
+ | expression WS+? AND WS+? expression        # EXPR_AND_EXPR
+ | expression WS+? OR WS+ expression          # EXPR_OR_EXPR
+ | expression WS+? operator WS+? expression   # EXPR_OPER_EXPR
+ | ATTRNAME WS+? PR                           # ATTR_PR
+ | ATTRNAME WS+? operator WS+? expression     # ATTR_OPER_EXPR
+ | ATTRNAME WS+? operator WS+? criteria       # ATTR_OPER_CRITERIA
+ | LPAREN WS*? expression WS*? RPAREN         # LPAREN_EXPR_RPAREN
+ | ATTRNAME LBRAC WS*? expression WS*? RBRAC  # LBRAC_EXPR_RBRAC
+ ;
+
+criteria : '"' .+? '"';
+
+operator
+ : EQ | NE | CO | SW | EW | GT | LT | GE | LE
+ ;
+
+EQ : [eE][qQ];
+NE : [nN][eE];
+CO : [cC][oO];
+SW : [sS][wW];
+EW : [eE][wW];
+GT : [gG][tT];
+LT : [lL][tT];
+GE : [gG][eE];
+LE : [lL][eE];
+
+NOT : [nN][oO][tT];
+
+AND : [aA][nN][dD];
+OR  : [oO][rR];
+
+PR : [pP][rR];
+
+LPAREN : '(';
+RPAREN : ')';
+
+LBRAC : '[';
+RBRAC : ']';
+
+WS : ' ';
+
+ATTRNAME : [-_.:a-zA-Z0-9]+;
+
+ANY : ~('"' | '(' | ')' | '[' | ']');
+
+EOL : [\t\r\n\u000C]+ -> skip;

http://git-wip-us.apache.org/repos/asf/syncope/blob/4989ffd0/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SCIMFilterErrorHandler.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SCIMFilterErrorHandler.java b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SCIMFilterErrorHandler.java
new file mode 100644
index 0000000..db2bfbd
--- /dev/null
+++ b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SCIMFilterErrorHandler.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic.scim;
+
+import org.antlr.v4.runtime.DefaultErrorStrategy;
+import org.antlr.v4.runtime.InputMismatchException;
+import org.antlr.v4.runtime.Parser;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Token;
+
+public class SCIMFilterErrorHandler extends DefaultErrorStrategy {
+
+    @Override
+    public void recover(final Parser recognizer, final RecognitionException e) {
+        throw e;
+    }
+
+    @Override
+    public Token recoverInline(final Parser recognizer) throws RecognitionException {
+        throw new InputMismatchException(recognizer);
+    }
+
+    @Override
+    public void sync(final Parser recognizer) throws RecognitionException {
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/4989ffd0/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
new file mode 100644
index 0000000..fd45f43
--- /dev/null
+++ b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SearchCondConverter.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic.scim;
+
+import org.antlr.v4.runtime.CharStreams;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.ext.scimv2.api.SCIMBadRequestException;
+import org.apache.syncope.ext.scimv2.api.type.ErrorType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Converts SCIM filter expressions to Syncope's {@link SearchCond}.
+ */
+public final class SearchCondConverter {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SearchCondConverter.class);
+
+    public static SearchCond convert(final String filter) {
+        SCIMFilterParser parser = new SCIMFilterParser(new CommonTokenStream(
+                new SCIMFilterLexer(CharStreams.fromString(filter))));
+        parser.setBuildParseTree(true);
+        parser.setTrimParseTree(true);
+        parser.setProfile(true);
+        parser.removeErrorListeners();
+        parser.setErrorHandler(new SCIMFilterErrorHandler());
+
+        try {
+            return new SearchCondVisitor().visit(parser.scimFilter());
+        } catch (Exception e) {
+            LOG.error("Could not parse {}", filter, e);
+            throw new SCIMBadRequestException(ErrorType.invalidFilter, e.getMessage());
+        }
+    }
+
+    private SearchCondConverter() {
+        // empty constructor for static utility class        
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/4989ffd0/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
new file mode 100644
index 0000000..1882505
--- /dev/null
+++ b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SearchCondVisitor.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic.scim;
+
+import org.apache.commons.lang3.StringUtils;
+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;
+import org.apache.syncope.ext.scimv2.api.type.Resource;
+
+/**
+ * Visits SCIM filter expression and produces {@link SearchCond}.
+ */
+public class SearchCondVisitor extends SCIMFilterBaseVisitor<SearchCond> {
+
+    @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)) {
+
+            attributeCond = new AnyCond();
+            attributeCond.setSchema("username");
+        } else if ("displayName".equalsIgnoreCase(schema)
+                || (Resource.Group.schema() + ":displayName").equalsIgnoreCase(schema)) {
+
+            attributeCond = new AnyCond();
+            attributeCond.setSchema("name");
+        } else if ("meta.created".equals(schema)) {
+            attributeCond = new AnyCond();
+            attributeCond.setSchema("creationDate");
+        } else if ("meta.lastModified".equals(schema)) {
+            attributeCond = new AnyCond();
+            attributeCond.setSchema("lastChangeDate");
+        } else {
+            attributeCond = new AttributeCond();
+            attributeCond.setSchema(schema);
+        }
+
+        return attributeCond;
+    }
+
+    private SearchCond transform(final String operator, final String left, final String right) {
+        AttributeCond attributeCond = createAttributeCond(left);
+        attributeCond.setExpression(StringUtils.strip(right, "\""));
+
+        switch (operator) {
+            case "eq":
+            default:
+                attributeCond.setType(AttributeCond.Type.IEQ);
+                break;
+
+            case "ne":
+                attributeCond.setType(AttributeCond.Type.IEQ);
+                break;
+
+            case "sw":
+                attributeCond.setType(AttributeCond.Type.ILIKE);
+                attributeCond.setExpression(attributeCond.getExpression() + "%");
+                break;
+
+            case "co":
+                attributeCond.setType(AttributeCond.Type.ILIKE);
+                attributeCond.setExpression("%" + attributeCond.getExpression() + "%");
+                break;
+
+            case "ew":
+                attributeCond.setType(AttributeCond.Type.ILIKE);
+                attributeCond.setExpression("%" + attributeCond.getExpression());
+                break;
+
+            case "gt":
+                attributeCond.setType(AttributeCond.Type.GT);
+                break;
+
+            case "ge":
+                attributeCond.setType(AttributeCond.Type.GE);
+                break;
+
+            case "lt":
+                attributeCond.setType(AttributeCond.Type.LT);
+                break;
+
+            case "le":
+                attributeCond.setType(AttributeCond.Type.LE);
+                break;
+
+        }
+
+        return "ne".equals(operator)
+                ? SearchCond.getNotLeafCond(attributeCond)
+                : SearchCond.getLeafCond(attributeCond);
+    }
+
+    @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());
+    }
+
+    @Override
+    public SearchCond visitATTR_OPER_CRITERIA(final SCIMFilterParser.ATTR_OPER_CRITERIAContext ctx) {
+        return transform(ctx.operator().getText(), ctx.ATTRNAME().getText(), ctx.criteria().getText());
+    }
+
+    @Override
+    public SearchCond visitATTR_OPER_EXPR(final SCIMFilterParser.ATTR_OPER_EXPRContext ctx) {
+        return transform(ctx.operator().getText(), ctx.ATTRNAME().getText(), ctx.expression().getText());
+    }
+
+    @Override
+    public SearchCond visitATTR_PR(final SCIMFilterParser.ATTR_PRContext ctx) {
+        AttributeCond cond = createAttributeCond(ctx.ATTRNAME().getText());
+        cond.setType(AttributeCond.Type.ISNOTNULL);
+        return SearchCond.getLeafCond(cond);
+    }
+
+    @Override
+    public SearchCond visitLPAREN_EXPR_RPAREN(final SCIMFilterParser.LPAREN_EXPR_RPARENContext ctx) {
+        return visit(ctx.expression());
+    }
+
+    @Override
+    public SearchCond visitNOT_EXPR(final SCIMFilterParser.NOT_EXPRContext ctx) {
+        SearchCond cond = visit(ctx.expression());
+        if (cond.getAttributeCond() != null) {
+            if (cond.getAttributeCond().getType() == AttributeCond.Type.ISNULL) {
+                cond.getAttributeCond().setType(AttributeCond.Type.ISNOTNULL);
+            } else if (cond.getAttributeCond().getType() == AttributeCond.Type.ISNOTNULL) {
+                cond.getAttributeCond().setType(AttributeCond.Type.ISNULL);
+            }
+        } else if (cond.getAnyCond() != null) {
+            if (cond.getAnyCond().getType() == AnyCond.Type.ISNULL) {
+                cond.getAnyCond().setType(AnyCond.Type.ISNOTNULL);
+            } else if (cond.getAnyCond().getType() == AnyCond.Type.ISNOTNULL) {
+                cond.getAnyCond().setType(AnyCond.Type.ISNULL);
+            }
+        } else {
+            cond = SearchCond.getNotLeafCond(cond);
+        }
+
+        return cond;
+    }
+
+    @Override
+    public SearchCond visitEXPR_AND_EXPR(final SCIMFilterParser.EXPR_AND_EXPRContext ctx) {
+        return SearchCond.getAndCond(visit(ctx.expression(0)), visit(ctx.expression(1)));
+    }
+
+    @Override
+    public SearchCond visitEXPR_OR_EXPR(final SCIMFilterParser.EXPR_OR_EXPRContext ctx) {
+        return SearchCond.getOrCond(visit(ctx.expression(0)), visit(ctx.expression(1)));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/4989ffd0/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
new file mode 100644
index 0000000..d488725
--- /dev/null
+++ b/ext/scimv2/logic/src/test/java/org/apache/syncope/core/logic/scim/SCIMFilterTest.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic.scim;
+
+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.core.persistence.api.dao.search.AttributeCond;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.junit.jupiter.api.Test;
+
+public class SCIMFilterTest {
+
+    @Test
+    public void eq() {
+        SearchCond cond = SearchCondConverter.convert("userName eq \"bjensen\"");
+        assertNotNull(cond);
+        assertNotNull(cond.getAnyCond());
+        assertEquals("username", cond.getAnyCond().getSchema());
+        assertEquals(AttributeCond.Type.IEQ, cond.getAnyCond().getType());
+        assertEquals("bjensen", cond.getAnyCond().getExpression());
+    }
+
+    @Test
+    public void sw() {
+        SearchCond cond = SearchCondConverter.convert("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\"");
+        assertEquals(cond, fqn);
+    }
+
+    @Test
+    public void pr() {
+        SearchCond cond = SearchCondConverter.convert("title pr");
+        assertNotNull(cond);
+        assertNotNull(cond.getAttributeCond());
+        assertEquals("title", cond.getAttributeCond().getSchema());
+        assertEquals(AttributeCond.Type.ISNOTNULL, cond.getAttributeCond().getType());
+        assertNull(cond.getAttributeCond().getExpression());
+    }
+
+    @Test
+    public void gt() {
+        SearchCond cond = SearchCondConverter.convert("meta.lastModified gt \"2011-05-13T04:42:34Z\"");
+        assertNotNull(cond);
+        assertNotNull(cond.getAnyCond());
+        assertEquals("lastChangeDate", cond.getAnyCond().getSchema());
+        assertEquals(AttributeCond.Type.GT, cond.getAnyCond().getType());
+        assertEquals("2011-05-13T04:42:34Z", cond.getAnyCond().getExpression());
+    }
+
+    @Test
+    public void not() {
+        SearchCond cond = SearchCondConverter.convert("not (title pr)");
+        assertNotNull(cond);
+        assertNotNull(cond.getAttributeCond());
+        assertEquals("title", cond.getAttributeCond().getSchema());
+        assertEquals(AttributeCond.Type.ISNULL, cond.getAttributeCond().getType());
+        assertNull(cond.getAttributeCond().getExpression());
+    }
+
+    @Test
+    public void and() {
+        SearchCond cond = SearchCondConverter.convert("title pr and userName sw \"J\"");
+        assertNotNull(cond);
+        assertEquals(SearchCond.Type.AND, cond.getType());
+
+        SearchCond left = cond.getLeftSearchCond();
+        assertNotNull(left);
+        assertNotNull(left.getAttributeCond());
+        assertEquals("title", left.getAttributeCond().getSchema());
+        assertEquals(AttributeCond.Type.ISNOTNULL, left.getAttributeCond().getType());
+        assertNull(left.getAttributeCond().getExpression());
+
+        SearchCond right = cond.getRightSearchCond();
+        assertNotNull(right);
+        assertNotNull(right.getAnyCond());
+        assertEquals("username", right.getAnyCond().getSchema());
+        assertEquals(AttributeCond.Type.ILIKE, right.getAnyCond().getType());
+        assertEquals("J%", right.getAnyCond().getExpression());
+    }
+
+    @Test
+    public void or() {
+        SearchCond cond = SearchCondConverter.convert("title pr or displayName eq \"Other\"");
+        assertNotNull(cond);
+        assertEquals(SearchCond.Type.OR, cond.getType());
+
+        SearchCond left = cond.getLeftSearchCond();
+        assertNotNull(left);
+        assertNotNull(left.getAttributeCond());
+        assertEquals("title", left.getAttributeCond().getSchema());
+        assertEquals(AttributeCond.Type.ISNOTNULL, left.getAttributeCond().getType());
+        assertNull(left.getAttributeCond().getExpression());
+
+        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());
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/4989ffd0/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Group.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Group.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Group.java
index f64b837..f84d559 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Group.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Group.java
@@ -39,7 +39,7 @@ public class Group extends Reference {
             @JsonProperty("display") final String display,
             @JsonProperty("type") final Function type) {
 
-        super(value, ref, display);
+        super(value, display, ref);
         this.type = type;
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/4989ffd0/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Member.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Member.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Member.java
index aa2f5dd..902217d 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Member.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Member.java
@@ -37,7 +37,7 @@ public class Member extends Reference {
             @JsonProperty("display") final String display,
             @JsonProperty("type") final Resource type) {
 
-        super(value, ref, display);
+        super(value, display, ref);
         this.type = type;
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/4989ffd0/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMError.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMError.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMError.java
index d7112d1..f0548c4 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMError.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMError.java
@@ -24,6 +24,7 @@ import com.fasterxml.jackson.annotation.JsonFormat.Shape;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import java.util.Arrays;
 import java.util.List;
+import org.apache.syncope.ext.scimv2.api.SCIMBadRequestException;
 import org.apache.syncope.ext.scimv2.api.type.ErrorType;
 import org.apache.syncope.ext.scimv2.api.type.Resource;
 
@@ -40,6 +41,10 @@ public class SCIMError extends SCIMBean {
     @JsonFormat(shape = Shape.STRING)
     private final int status = 400;
 
+    public SCIMError(final SCIMBadRequestException ex) {
+        this(ex.getErrorType(), ex.getMessage());
+    }
+
     @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
     public SCIMError(
             @JsonProperty("scimType") final ErrorType scimType,

http://git-wip-us.apache.org/repos/asf/syncope/blob/4989ffd0/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java
index e525e77..4525d82 100644
--- a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java
+++ b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java
@@ -41,6 +41,7 @@ import org.apache.syncope.core.spring.security.DelegatedAdministrationException;
 import org.apache.syncope.core.workflow.api.WorkflowException;
 import org.apache.syncope.ext.scimv2.api.ConflictException;
 import org.apache.syncope.ext.scimv2.api.PayloadTooLargeException;
+import org.apache.syncope.ext.scimv2.api.SCIMBadRequestException;
 import org.apache.syncope.ext.scimv2.api.data.SCIMError;
 import org.apache.syncope.ext.scimv2.api.type.ErrorType;
 import org.identityconnectors.framework.common.exceptions.ConfigurationException;
@@ -108,9 +109,7 @@ public class SCIMExceptionMapper implements ExceptionMapper<Exception> {
                 && ENTITYEXISTS_EXCLASS.isAssignableFrom(ex.getCause().getClass())) {
 
             builder = builder(ClientExceptionType.EntityExists, ExceptionUtils.getRootCauseMessage(ex));
-        } else if (ex instanceof DataIntegrityViolationException
-                || JPASYSTEM_EXCLASS.isAssignableFrom(ex.getClass())) {
-
+        } else if (ex instanceof DataIntegrityViolationException || JPASYSTEM_EXCLASS.isAssignableFrom(ex.getClass())) {
             builder = builder(ClientExceptionType.DataIntegrityViolation, ExceptionUtils.getRootCauseMessage(ex));
         } else if (CONNECTOR_EXCLASS.isAssignableFrom(ex.getClass())) {
             builder = builder(ClientExceptionType.ConnectorException, ExceptionUtils.getRootCauseMessage(ex));
@@ -125,7 +124,8 @@ public class SCIMExceptionMapper implements ExceptionMapper<Exception> {
             }
             // ...or just report as InternalServerError
             if (builder == null) {
-                builder = Response.status(Response.Status.INTERNAL_SERVER_ERROR);
+                builder = Response.status(Response.Status.INTERNAL_SERVER_ERROR).
+                        entity(ExceptionUtils.getRootCauseMessage(ex));
             }
         }
 
@@ -188,6 +188,8 @@ public class SCIMExceptionMapper implements ExceptionMapper<Exception> {
             return builder(ClientExceptionType.InvalidValues, ExceptionUtils.getRootCauseMessage(ex));
         } else if (ex instanceof MalformedPathException) {
             return builder(ClientExceptionType.InvalidPath, ExceptionUtils.getRootCauseMessage(ex));
+        } else if (ex instanceof SCIMBadRequestException) {
+            return Response.status(Response.Status.BAD_REQUEST).entity(new SCIMError((SCIMBadRequestException) ex));
         }
 
         return null;

http://git-wip-us.apache.org/repos/asf/syncope/blob/4989ffd0/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractSCIMService.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractSCIMService.java b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractSCIMService.java
index cd31e1f..73dbd5a 100644
--- a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractSCIMService.java
+++ b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractSCIMService.java
@@ -32,6 +32,7 @@ import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.core.logic.AbstractAnyLogic;
 import org.apache.syncope.core.logic.GroupLogic;
 import org.apache.syncope.core.logic.UserLogic;
+import org.apache.syncope.core.logic.scim.SearchCondConverter;
 import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
@@ -189,11 +190,9 @@ abstract class AbstractSCIMService<R extends SCIMResource> implements SCIMServic
             throw new UnsupportedOperationException();
         }
 
-        int page = startIndex == null || startIndex <= 1 ? 1 : (startIndex / AnyDAO.DEFAULT_PAGE_SIZE) + 1;
-
         Pair<Integer, ? extends List<? extends AnyTO>> result = anyLogic(type).search(
-                null,
-                page,
+                StringUtils.isBlank(filter) ? null : SearchCondConverter.convert(filter),
+                startIndex == null || startIndex <= 1 ? 1 : (startIndex / AnyDAO.DEFAULT_PAGE_SIZE) + 1,
                 AnyDAO.DEFAULT_PAGE_SIZE,
                 Collections.<OrderByClause>emptyList(),
                 SyncopeConstants.ROOT_REALM,

http://git-wip-us.apache.org/repos/asf/syncope/blob/4989ffd0/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 ecc75ba..cc8a92e 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
@@ -27,13 +27,18 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import java.io.IOException;
+import java.text.SimpleDateFormat;
 import java.util.Arrays;
+import java.util.Date;
 import java.util.List;
 import javax.ws.rs.core.GenericType;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.to.ProvisioningResult;
+import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.ext.scimv2.api.SCIMConstants;
 import org.apache.syncope.ext.scimv2.api.data.ListResponse;
 import org.apache.syncope.ext.scimv2.api.data.ResourceType;
@@ -50,6 +55,16 @@ public class SCIMITCase extends AbstractITCase {
 
     public static final String SCIM_ADDRESS = "http://localhost:9080/syncope/scim/v2";
 
+    private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT = new ThreadLocal<SimpleDateFormat>() {
+
+        @Override
+        protected SimpleDateFormat initialValue() {
+            SimpleDateFormat sdf = new SimpleDateFormat();
+            sdf.applyPattern(SyncopeConstants.DEFAULT_DATE_PATTERN);
+            return sdf;
+        }
+    };
+
     private WebClient webClient() {
         return WebClient.create(SCIM_ADDRESS, Arrays.asList(new JacksonSCIMJsonProvider())).
                 accept(SCIMConstants.APPLICATION_SCIM_JSON_TYPE).
@@ -159,4 +174,45 @@ public class SCIMITCase extends AbstractITCase {
             assertNotNull(group.getDisplayName());
         });
     }
+
+    @Test
+    public void search() {
+        assumeTrue(SCIMDetector.isSCIMAvailable(webClient()));
+
+        // eq
+        Response response = webClient().path("Groups").query("filter", "displayName eq \"additional\"").get();
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+        assertEquals(
+                SCIMConstants.APPLICATION_SCIM_JSON,
+                StringUtils.substringBefore(response.getHeaderString(HttpHeaders.CONTENT_TYPE), ";"));
+
+        ListResponse<SCIMGroup> groups = response.readEntity(new GenericType<ListResponse<SCIMGroup>>() {
+        });
+        assertNotNull(groups);
+        assertEquals(1, groups.getTotalResults());
+
+        SCIMGroup additional = groups.getResources().get(0);
+        assertEquals("additional", additional.getDisplayName());
+
+        // gt
+        UserTO newUser = userService.create(UserITCase.getUniqueSampleTO("scimsearch@syncope.apache.org")).readEntity(
+                new GenericType<ProvisioningResult<UserTO>>() {
+        }).getEntity();
+
+        Date value = new Date(newUser.getCreationDate().getTime() - 1000);
+        response = webClient().path("Users").query("filter", "meta.created gt \""
+                + DATE_FORMAT.get().format(value) + "\"").get();
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+        assertEquals(
+                SCIMConstants.APPLICATION_SCIM_JSON,
+                StringUtils.substringBefore(response.getHeaderString(HttpHeaders.CONTENT_TYPE), ";"));
+
+        ListResponse<SCIMUser> users = response.readEntity(new GenericType<ListResponse<SCIMUser>>() {
+        });
+        assertNotNull(users);
+        assertEquals(1, users.getTotalResults());
+
+        SCIMUser newSCIMUser = users.getResources().get(0);
+        assertEquals(newUser.getUsername(), newSCIMUser.getUserName());
+    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/4989ffd0/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 51cfd7f..c8f6e52 100644
--- a/pom.xml
+++ b/pom.xml
@@ -462,6 +462,8 @@ under the License.
     <tycho.version>1.0.0</tycho.version>
     <netbeans.version>RELEASE82</netbeans.version>
 
+    <antlr4.version>4.7</antlr4.version>
+
     <testds.port>1389</testds.port>
     <testdb.webport>9082</testdb.webport>
 
@@ -1569,6 +1571,12 @@ under the License.
         <version>${netbeans.version}</version>
       </dependency>
 
+      <dependency>
+        <groupId>org.antlr</groupId>
+        <artifactId>antlr4-runtime</artifactId>
+        <version>${antlr4.version}</version>
+      </dependency>
+
       <!-- TEST -->
       <dependency>
         <groupId>com.github.detro</groupId>
@@ -1935,6 +1943,12 @@ under the License.
           <artifactId>exec-maven-plugin</artifactId>
           <version>1.6.0</version>
         </plugin>
+        
+        <plugin>
+          <groupId>org.antlr</groupId>
+          <artifactId>antlr4-maven-plugin</artifactId>
+          <version>${antlr4.version}</version>
+        </plugin>
       </plugins>
     </pluginManagement>
 


[3/6] syncope git commit: [SYNCOPE-152] SCIM filter

Posted by il...@apache.org.
[SYNCOPE-152] SCIM filter


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

Branch: refs/heads/2_0_X
Commit: 1c469ae175d50acd88282b6bb7081e453157bb6f
Parents: 99a9f1a
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Mon Nov 6 13:50:09 2017 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Mon Nov 6 13:50:09 2017 +0100

----------------------------------------------------------------------
 ext/scimv2/logic/pom.xml                        |  27 +++
 .../syncope/core/logic/scim/SCIMFilter.g4       |  77 ++++++++
 .../core/logic/scim/SCIMFilterErrorHandler.java |  42 +++++
 .../core/logic/scim/SearchCondConverter.java    |  56 ++++++
 .../core/logic/scim/SearchCondVisitor.java      | 174 +++++++++++++++++++
 .../syncope/core/logic/scim/SCIMFilterTest.java | 125 +++++++++++++
 .../syncope/ext/scimv2/api/data/Group.java      |   2 +-
 .../syncope/ext/scimv2/api/data/Member.java     |   2 +-
 .../syncope/ext/scimv2/api/data/SCIMError.java  |   5 +
 .../ext/scimv2/cxf/SCIMExceptionMapper.java     |  10 +-
 .../scimv2/cxf/service/AbstractSCIMService.java |   7 +-
 .../org/apache/syncope/fit/core/SCIMITCase.java |  56 ++++++
 pom.xml                                         |  14 ++
 13 files changed, 587 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/1c469ae1/ext/scimv2/logic/pom.xml
----------------------------------------------------------------------
diff --git a/ext/scimv2/logic/pom.xml b/ext/scimv2/logic/pom.xml
index b127fb8..188d71e 100644
--- a/ext/scimv2/logic/pom.xml
+++ b/ext/scimv2/logic/pom.xml
@@ -35,6 +35,7 @@ under the License.
   
   <properties>
     <rootpom.basedir>${basedir}/../../..</rootpom.basedir>
+    <antlr4.visitor>true</antlr4.visitor>
   </properties>
 
   <dependencies>
@@ -49,6 +50,17 @@ under the License.
       <artifactId>syncope-ext-scimv2-scim-rest-api</artifactId>
       <version>${project.version}</version>
     </dependency>
+    
+    <dependency>
+      <groupId>org.antlr</groupId>
+      <artifactId>antlr4-runtime</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
@@ -56,6 +68,21 @@ under the License.
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-checkstyle-plugin</artifactId>
+        <configuration>
+          <sourceDirectory>${project.build.sourceDirectory}</sourceDirectory>
+        </configuration>
+      </plugin>
+      
+      <plugin>
+        <groupId>org.antlr</groupId>
+        <artifactId>antlr4-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>antlr4</goal>
+            </goals>
+          </execution>
+        </executions>
       </plugin>
     </plugins>
   </build>

http://git-wip-us.apache.org/repos/asf/syncope/blob/1c469ae1/ext/scimv2/logic/src/main/antlr4/org/apache/syncope/core/logic/scim/SCIMFilter.g4
----------------------------------------------------------------------
diff --git a/ext/scimv2/logic/src/main/antlr4/org/apache/syncope/core/logic/scim/SCIMFilter.g4 b/ext/scimv2/logic/src/main/antlr4/org/apache/syncope/core/logic/scim/SCIMFilter.g4
new file mode 100644
index 0000000..ffddf32
--- /dev/null
+++ b/ext/scimv2/logic/src/main/antlr4/org/apache/syncope/core/logic/scim/SCIMFilter.g4
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+grammar SCIMFilter;
+
+options
+{
+  language = Java;
+}
+
+scimFilter
+ : expression* EOF
+ ;
+
+expression
+ : NOT WS+? expression                        # NOT_EXPR
+ | expression WS+? AND WS+? expression        # EXPR_AND_EXPR
+ | expression WS+? OR WS+ expression          # EXPR_OR_EXPR
+ | expression WS+? operator WS+? expression   # EXPR_OPER_EXPR
+ | ATTRNAME WS+? PR                           # ATTR_PR
+ | ATTRNAME WS+? operator WS+? expression     # ATTR_OPER_EXPR
+ | ATTRNAME WS+? operator WS+? criteria       # ATTR_OPER_CRITERIA
+ | LPAREN WS*? expression WS*? RPAREN         # LPAREN_EXPR_RPAREN
+ | ATTRNAME LBRAC WS*? expression WS*? RBRAC  # LBRAC_EXPR_RBRAC
+ ;
+
+criteria : '"' .+? '"';
+
+operator
+ : EQ | NE | CO | SW | EW | GT | LT | GE | LE
+ ;
+
+EQ : [eE][qQ];
+NE : [nN][eE];
+CO : [cC][oO];
+SW : [sS][wW];
+EW : [eE][wW];
+GT : [gG][tT];
+LT : [lL][tT];
+GE : [gG][eE];
+LE : [lL][eE];
+
+NOT : [nN][oO][tT];
+
+AND : [aA][nN][dD];
+OR  : [oO][rR];
+
+PR : [pP][rR];
+
+LPAREN : '(';
+RPAREN : ')';
+
+LBRAC : '[';
+RBRAC : ']';
+
+WS : ' ';
+
+ATTRNAME : [-_.:a-zA-Z0-9]+;
+
+ANY : ~('"' | '(' | ')' | '[' | ']');
+
+EOL : [\t\r\n\u000C]+ -> skip;

http://git-wip-us.apache.org/repos/asf/syncope/blob/1c469ae1/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SCIMFilterErrorHandler.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SCIMFilterErrorHandler.java b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SCIMFilterErrorHandler.java
new file mode 100644
index 0000000..db2bfbd
--- /dev/null
+++ b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SCIMFilterErrorHandler.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic.scim;
+
+import org.antlr.v4.runtime.DefaultErrorStrategy;
+import org.antlr.v4.runtime.InputMismatchException;
+import org.antlr.v4.runtime.Parser;
+import org.antlr.v4.runtime.RecognitionException;
+import org.antlr.v4.runtime.Token;
+
+public class SCIMFilterErrorHandler extends DefaultErrorStrategy {
+
+    @Override
+    public void recover(final Parser recognizer, final RecognitionException e) {
+        throw e;
+    }
+
+    @Override
+    public Token recoverInline(final Parser recognizer) throws RecognitionException {
+        throw new InputMismatchException(recognizer);
+    }
+
+    @Override
+    public void sync(final Parser recognizer) throws RecognitionException {
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/1c469ae1/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
new file mode 100644
index 0000000..fd45f43
--- /dev/null
+++ b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SearchCondConverter.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic.scim;
+
+import org.antlr.v4.runtime.CharStreams;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.ext.scimv2.api.SCIMBadRequestException;
+import org.apache.syncope.ext.scimv2.api.type.ErrorType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Converts SCIM filter expressions to Syncope's {@link SearchCond}.
+ */
+public final class SearchCondConverter {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SearchCondConverter.class);
+
+    public static SearchCond convert(final String filter) {
+        SCIMFilterParser parser = new SCIMFilterParser(new CommonTokenStream(
+                new SCIMFilterLexer(CharStreams.fromString(filter))));
+        parser.setBuildParseTree(true);
+        parser.setTrimParseTree(true);
+        parser.setProfile(true);
+        parser.removeErrorListeners();
+        parser.setErrorHandler(new SCIMFilterErrorHandler());
+
+        try {
+            return new SearchCondVisitor().visit(parser.scimFilter());
+        } catch (Exception e) {
+            LOG.error("Could not parse {}", filter, e);
+            throw new SCIMBadRequestException(ErrorType.invalidFilter, e.getMessage());
+        }
+    }
+
+    private SearchCondConverter() {
+        // empty constructor for static utility class        
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/1c469ae1/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
new file mode 100644
index 0000000..1882505
--- /dev/null
+++ b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SearchCondVisitor.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic.scim;
+
+import org.apache.commons.lang3.StringUtils;
+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;
+import org.apache.syncope.ext.scimv2.api.type.Resource;
+
+/**
+ * Visits SCIM filter expression and produces {@link SearchCond}.
+ */
+public class SearchCondVisitor extends SCIMFilterBaseVisitor<SearchCond> {
+
+    @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)) {
+
+            attributeCond = new AnyCond();
+            attributeCond.setSchema("username");
+        } else if ("displayName".equalsIgnoreCase(schema)
+                || (Resource.Group.schema() + ":displayName").equalsIgnoreCase(schema)) {
+
+            attributeCond = new AnyCond();
+            attributeCond.setSchema("name");
+        } else if ("meta.created".equals(schema)) {
+            attributeCond = new AnyCond();
+            attributeCond.setSchema("creationDate");
+        } else if ("meta.lastModified".equals(schema)) {
+            attributeCond = new AnyCond();
+            attributeCond.setSchema("lastChangeDate");
+        } else {
+            attributeCond = new AttributeCond();
+            attributeCond.setSchema(schema);
+        }
+
+        return attributeCond;
+    }
+
+    private SearchCond transform(final String operator, final String left, final String right) {
+        AttributeCond attributeCond = createAttributeCond(left);
+        attributeCond.setExpression(StringUtils.strip(right, "\""));
+
+        switch (operator) {
+            case "eq":
+            default:
+                attributeCond.setType(AttributeCond.Type.IEQ);
+                break;
+
+            case "ne":
+                attributeCond.setType(AttributeCond.Type.IEQ);
+                break;
+
+            case "sw":
+                attributeCond.setType(AttributeCond.Type.ILIKE);
+                attributeCond.setExpression(attributeCond.getExpression() + "%");
+                break;
+
+            case "co":
+                attributeCond.setType(AttributeCond.Type.ILIKE);
+                attributeCond.setExpression("%" + attributeCond.getExpression() + "%");
+                break;
+
+            case "ew":
+                attributeCond.setType(AttributeCond.Type.ILIKE);
+                attributeCond.setExpression("%" + attributeCond.getExpression());
+                break;
+
+            case "gt":
+                attributeCond.setType(AttributeCond.Type.GT);
+                break;
+
+            case "ge":
+                attributeCond.setType(AttributeCond.Type.GE);
+                break;
+
+            case "lt":
+                attributeCond.setType(AttributeCond.Type.LT);
+                break;
+
+            case "le":
+                attributeCond.setType(AttributeCond.Type.LE);
+                break;
+
+        }
+
+        return "ne".equals(operator)
+                ? SearchCond.getNotLeafCond(attributeCond)
+                : SearchCond.getLeafCond(attributeCond);
+    }
+
+    @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());
+    }
+
+    @Override
+    public SearchCond visitATTR_OPER_CRITERIA(final SCIMFilterParser.ATTR_OPER_CRITERIAContext ctx) {
+        return transform(ctx.operator().getText(), ctx.ATTRNAME().getText(), ctx.criteria().getText());
+    }
+
+    @Override
+    public SearchCond visitATTR_OPER_EXPR(final SCIMFilterParser.ATTR_OPER_EXPRContext ctx) {
+        return transform(ctx.operator().getText(), ctx.ATTRNAME().getText(), ctx.expression().getText());
+    }
+
+    @Override
+    public SearchCond visitATTR_PR(final SCIMFilterParser.ATTR_PRContext ctx) {
+        AttributeCond cond = createAttributeCond(ctx.ATTRNAME().getText());
+        cond.setType(AttributeCond.Type.ISNOTNULL);
+        return SearchCond.getLeafCond(cond);
+    }
+
+    @Override
+    public SearchCond visitLPAREN_EXPR_RPAREN(final SCIMFilterParser.LPAREN_EXPR_RPARENContext ctx) {
+        return visit(ctx.expression());
+    }
+
+    @Override
+    public SearchCond visitNOT_EXPR(final SCIMFilterParser.NOT_EXPRContext ctx) {
+        SearchCond cond = visit(ctx.expression());
+        if (cond.getAttributeCond() != null) {
+            if (cond.getAttributeCond().getType() == AttributeCond.Type.ISNULL) {
+                cond.getAttributeCond().setType(AttributeCond.Type.ISNOTNULL);
+            } else if (cond.getAttributeCond().getType() == AttributeCond.Type.ISNOTNULL) {
+                cond.getAttributeCond().setType(AttributeCond.Type.ISNULL);
+            }
+        } else if (cond.getAnyCond() != null) {
+            if (cond.getAnyCond().getType() == AnyCond.Type.ISNULL) {
+                cond.getAnyCond().setType(AnyCond.Type.ISNOTNULL);
+            } else if (cond.getAnyCond().getType() == AnyCond.Type.ISNOTNULL) {
+                cond.getAnyCond().setType(AnyCond.Type.ISNULL);
+            }
+        } else {
+            cond = SearchCond.getNotLeafCond(cond);
+        }
+
+        return cond;
+    }
+
+    @Override
+    public SearchCond visitEXPR_AND_EXPR(final SCIMFilterParser.EXPR_AND_EXPRContext ctx) {
+        return SearchCond.getAndCond(visit(ctx.expression(0)), visit(ctx.expression(1)));
+    }
+
+    @Override
+    public SearchCond visitEXPR_OR_EXPR(final SCIMFilterParser.EXPR_OR_EXPRContext ctx) {
+        return SearchCond.getOrCond(visit(ctx.expression(0)), visit(ctx.expression(1)));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/1c469ae1/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
new file mode 100644
index 0000000..08dbf1d
--- /dev/null
+++ b/ext/scimv2/logic/src/test/java/org/apache/syncope/core/logic/scim/SCIMFilterTest.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.logic.scim;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.junit.Test;
+
+public class SCIMFilterTest {
+
+    @Test
+    public void eq() {
+        SearchCond cond = SearchCondConverter.convert("userName eq \"bjensen\"");
+        assertNotNull(cond);
+        assertNotNull(cond.getAnyCond());
+        assertEquals("username", cond.getAnyCond().getSchema());
+        assertEquals(AttributeCond.Type.IEQ, cond.getAnyCond().getType());
+        assertEquals("bjensen", cond.getAnyCond().getExpression());
+    }
+
+    @Test
+    public void sw() {
+        SearchCond cond = SearchCondConverter.convert("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\"");
+        assertEquals(cond, fqn);
+    }
+
+    @Test
+    public void pr() {
+        SearchCond cond = SearchCondConverter.convert("title pr");
+        assertNotNull(cond);
+        assertNotNull(cond.getAttributeCond());
+        assertEquals("title", cond.getAttributeCond().getSchema());
+        assertEquals(AttributeCond.Type.ISNOTNULL, cond.getAttributeCond().getType());
+        assertNull(cond.getAttributeCond().getExpression());
+    }
+
+    @Test
+    public void gt() {
+        SearchCond cond = SearchCondConverter.convert("meta.lastModified gt \"2011-05-13T04:42:34Z\"");
+        assertNotNull(cond);
+        assertNotNull(cond.getAnyCond());
+        assertEquals("lastChangeDate", cond.getAnyCond().getSchema());
+        assertEquals(AttributeCond.Type.GT, cond.getAnyCond().getType());
+        assertEquals("2011-05-13T04:42:34Z", cond.getAnyCond().getExpression());
+    }
+
+    @Test
+    public void not() {
+        SearchCond cond = SearchCondConverter.convert("not (title pr)");
+        assertNotNull(cond);
+        assertNotNull(cond.getAttributeCond());
+        assertEquals("title", cond.getAttributeCond().getSchema());
+        assertEquals(AttributeCond.Type.ISNULL, cond.getAttributeCond().getType());
+        assertNull(cond.getAttributeCond().getExpression());
+    }
+
+    @Test
+    public void and() {
+        SearchCond cond = SearchCondConverter.convert("title pr and userName sw \"J\"");
+        assertNotNull(cond);
+        assertEquals(SearchCond.Type.AND, cond.getType());
+
+        SearchCond left = cond.getLeftSearchCond();
+        assertNotNull(left);
+        assertNotNull(left.getAttributeCond());
+        assertEquals("title", left.getAttributeCond().getSchema());
+        assertEquals(AttributeCond.Type.ISNOTNULL, left.getAttributeCond().getType());
+        assertNull(left.getAttributeCond().getExpression());
+
+        SearchCond right = cond.getRightSearchCond();
+        assertNotNull(right);
+        assertNotNull(right.getAnyCond());
+        assertEquals("username", right.getAnyCond().getSchema());
+        assertEquals(AttributeCond.Type.ILIKE, right.getAnyCond().getType());
+        assertEquals("J%", right.getAnyCond().getExpression());
+    }
+
+    @Test
+    public void or() {
+        SearchCond cond = SearchCondConverter.convert("title pr or displayName eq \"Other\"");
+        assertNotNull(cond);
+        assertEquals(SearchCond.Type.OR, cond.getType());
+
+        SearchCond left = cond.getLeftSearchCond();
+        assertNotNull(left);
+        assertNotNull(left.getAttributeCond());
+        assertEquals("title", left.getAttributeCond().getSchema());
+        assertEquals(AttributeCond.Type.ISNOTNULL, left.getAttributeCond().getType());
+        assertNull(left.getAttributeCond().getExpression());
+
+        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());
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/1c469ae1/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Group.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Group.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Group.java
index f64b837..f84d559 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Group.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Group.java
@@ -39,7 +39,7 @@ public class Group extends Reference {
             @JsonProperty("display") final String display,
             @JsonProperty("type") final Function type) {
 
-        super(value, ref, display);
+        super(value, display, ref);
         this.type = type;
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/1c469ae1/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Member.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Member.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Member.java
index aa2f5dd..902217d 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Member.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/Member.java
@@ -37,7 +37,7 @@ public class Member extends Reference {
             @JsonProperty("display") final String display,
             @JsonProperty("type") final Resource type) {
 
-        super(value, ref, display);
+        super(value, display, ref);
         this.type = type;
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/1c469ae1/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMError.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMError.java b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMError.java
index d7112d1..f0548c4 100644
--- a/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMError.java
+++ b/ext/scimv2/scim-rest-api/src/main/java/org/apache/syncope/ext/scimv2/api/data/SCIMError.java
@@ -24,6 +24,7 @@ import com.fasterxml.jackson.annotation.JsonFormat.Shape;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import java.util.Arrays;
 import java.util.List;
+import org.apache.syncope.ext.scimv2.api.SCIMBadRequestException;
 import org.apache.syncope.ext.scimv2.api.type.ErrorType;
 import org.apache.syncope.ext.scimv2.api.type.Resource;
 
@@ -40,6 +41,10 @@ public class SCIMError extends SCIMBean {
     @JsonFormat(shape = Shape.STRING)
     private final int status = 400;
 
+    public SCIMError(final SCIMBadRequestException ex) {
+        this(ex.getErrorType(), ex.getMessage());
+    }
+
     @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
     public SCIMError(
             @JsonProperty("scimType") final ErrorType scimType,

http://git-wip-us.apache.org/repos/asf/syncope/blob/1c469ae1/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java
index e525e77..4525d82 100644
--- a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java
+++ b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMExceptionMapper.java
@@ -41,6 +41,7 @@ import org.apache.syncope.core.spring.security.DelegatedAdministrationException;
 import org.apache.syncope.core.workflow.api.WorkflowException;
 import org.apache.syncope.ext.scimv2.api.ConflictException;
 import org.apache.syncope.ext.scimv2.api.PayloadTooLargeException;
+import org.apache.syncope.ext.scimv2.api.SCIMBadRequestException;
 import org.apache.syncope.ext.scimv2.api.data.SCIMError;
 import org.apache.syncope.ext.scimv2.api.type.ErrorType;
 import org.identityconnectors.framework.common.exceptions.ConfigurationException;
@@ -108,9 +109,7 @@ public class SCIMExceptionMapper implements ExceptionMapper<Exception> {
                 && ENTITYEXISTS_EXCLASS.isAssignableFrom(ex.getCause().getClass())) {
 
             builder = builder(ClientExceptionType.EntityExists, ExceptionUtils.getRootCauseMessage(ex));
-        } else if (ex instanceof DataIntegrityViolationException
-                || JPASYSTEM_EXCLASS.isAssignableFrom(ex.getClass())) {
-
+        } else if (ex instanceof DataIntegrityViolationException || JPASYSTEM_EXCLASS.isAssignableFrom(ex.getClass())) {
             builder = builder(ClientExceptionType.DataIntegrityViolation, ExceptionUtils.getRootCauseMessage(ex));
         } else if (CONNECTOR_EXCLASS.isAssignableFrom(ex.getClass())) {
             builder = builder(ClientExceptionType.ConnectorException, ExceptionUtils.getRootCauseMessage(ex));
@@ -125,7 +124,8 @@ public class SCIMExceptionMapper implements ExceptionMapper<Exception> {
             }
             // ...or just report as InternalServerError
             if (builder == null) {
-                builder = Response.status(Response.Status.INTERNAL_SERVER_ERROR);
+                builder = Response.status(Response.Status.INTERNAL_SERVER_ERROR).
+                        entity(ExceptionUtils.getRootCauseMessage(ex));
             }
         }
 
@@ -188,6 +188,8 @@ public class SCIMExceptionMapper implements ExceptionMapper<Exception> {
             return builder(ClientExceptionType.InvalidValues, ExceptionUtils.getRootCauseMessage(ex));
         } else if (ex instanceof MalformedPathException) {
             return builder(ClientExceptionType.InvalidPath, ExceptionUtils.getRootCauseMessage(ex));
+        } else if (ex instanceof SCIMBadRequestException) {
+            return Response.status(Response.Status.BAD_REQUEST).entity(new SCIMError((SCIMBadRequestException) ex));
         }
 
         return null;

http://git-wip-us.apache.org/repos/asf/syncope/blob/1c469ae1/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractSCIMService.java
----------------------------------------------------------------------
diff --git a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractSCIMService.java b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractSCIMService.java
index 41703a2..f2361d2 100644
--- a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractSCIMService.java
+++ b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/AbstractSCIMService.java
@@ -33,6 +33,7 @@ import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.core.logic.AbstractAnyLogic;
 import org.apache.syncope.core.logic.GroupLogic;
 import org.apache.syncope.core.logic.UserLogic;
+import org.apache.syncope.core.logic.scim.SearchCondConverter;
 import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
@@ -190,11 +191,9 @@ abstract class AbstractSCIMService<R extends SCIMResource> implements SCIMServic
             throw new UnsupportedOperationException();
         }
 
-        int page = startIndex == null || startIndex <= 1 ? 1 : (startIndex / AnyDAO.DEFAULT_PAGE_SIZE) + 1;
-
         Pair<Integer, ? extends List<? extends AnyTO>> result = anyLogic(type).search(
-                null,
-                page,
+                StringUtils.isBlank(filter) ? null : SearchCondConverter.convert(filter),
+                startIndex == null || startIndex <= 1 ? 1 : (startIndex / AnyDAO.DEFAULT_PAGE_SIZE) + 1,
                 AnyDAO.DEFAULT_PAGE_SIZE,
                 Collections.<OrderByClause>emptyList(),
                 SyncopeConstants.ROOT_REALM,

http://git-wip-us.apache.org/repos/asf/syncope/blob/1c469ae1/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 2031da8..d7e19a2 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
@@ -26,13 +26,18 @@ import static org.junit.Assert.assertTrue;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import java.io.IOException;
+import java.text.SimpleDateFormat;
 import java.util.Arrays;
+import java.util.Date;
 import java.util.List;
 import javax.ws.rs.core.GenericType;
 import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.to.ProvisioningResult;
+import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.ext.scimv2.api.SCIMConstants;
 import org.apache.syncope.ext.scimv2.api.data.ListResponse;
 import org.apache.syncope.ext.scimv2.api.data.ResourceType;
@@ -50,6 +55,16 @@ public class SCIMITCase extends AbstractITCase {
 
     public static final String SCIM_ADDRESS = "http://localhost:9080/syncope/scim/v2";
 
+    private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT = new ThreadLocal<SimpleDateFormat>() {
+
+        @Override
+        protected SimpleDateFormat initialValue() {
+            SimpleDateFormat sdf = new SimpleDateFormat();
+            sdf.applyPattern(SyncopeConstants.DEFAULT_DATE_PATTERN);
+            return sdf;
+        }
+    };
+
     private WebClient webClient() {
         return WebClient.create(SCIM_ADDRESS, Arrays.asList(new JacksonSCIMJsonProvider())).
                 accept(SCIMConstants.APPLICATION_SCIM_JSON_TYPE).
@@ -159,4 +174,45 @@ public class SCIMITCase extends AbstractITCase {
             assertNotNull(group.getDisplayName());
         }
     }
+
+    @Test
+    public void search() {
+        Assume.assumeTrue(SCIMDetector.isSCIMAvailable(webClient()));
+
+        // eq
+        Response response = webClient().path("Groups").query("filter", "displayName eq \"additional\"").get();
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+        assertEquals(
+                SCIMConstants.APPLICATION_SCIM_JSON,
+                StringUtils.substringBefore(response.getHeaderString(HttpHeaders.CONTENT_TYPE), ";"));
+
+        ListResponse<SCIMGroup> groups = response.readEntity(new GenericType<ListResponse<SCIMGroup>>() {
+        });
+        assertNotNull(groups);
+        assertEquals(1, groups.getTotalResults());
+
+        SCIMGroup additional = groups.getResources().get(0);
+        assertEquals("additional", additional.getDisplayName());
+
+        // gt
+        UserTO newUser = userService.create(UserITCase.getUniqueSampleTO("scimsearch@syncope.apache.org")).readEntity(
+                new GenericType<ProvisioningResult<UserTO>>() {
+        }).getEntity();
+
+        Date value = new Date(newUser.getCreationDate().getTime() - 1000);
+        response = webClient().path("Users").query("filter", "meta.created gt \""
+                + DATE_FORMAT.get().format(value) + "\"").get();
+        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+        assertEquals(
+                SCIMConstants.APPLICATION_SCIM_JSON,
+                StringUtils.substringBefore(response.getHeaderString(HttpHeaders.CONTENT_TYPE), ";"));
+
+        ListResponse<SCIMUser> users = response.readEntity(new GenericType<ListResponse<SCIMUser>>() {
+        });
+        assertNotNull(users);
+        assertEquals(1, users.getTotalResults());
+
+        SCIMUser newSCIMUser = users.getResources().get(0);
+        assertEquals(newUser.getUsername(), newSCIMUser.getUserName());
+    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/1c469ae1/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 9d96234..c3133e0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -466,6 +466,8 @@ under the License.
     <tycho.version>0.23.1</tycho.version>
     <netbeans.version>RELEASE82</netbeans.version>
 
+    <antlr4.version>4.7</antlr4.version>
+
     <testds.port>1389</testds.port>
     <testdb.webport>9082</testdb.webport>
 
@@ -1656,6 +1658,12 @@ under the License.
         <version>${netbeans.version}</version>
       </dependency>
 
+      <dependency>
+        <groupId>org.antlr</groupId>
+        <artifactId>antlr4-runtime</artifactId>
+        <version>${antlr4.version}</version>
+      </dependency>
+
       <!-- TEST -->
       <dependency>
         <groupId>com.github.detro</groupId>
@@ -1986,6 +1994,12 @@ under the License.
           <artifactId>exec-maven-plugin</artifactId>
           <version>1.6.0</version>
         </plugin>
+        
+        <plugin>
+          <groupId>org.antlr</groupId>
+          <artifactId>antlr4-maven-plugin</artifactId>
+          <version>${antlr4.version}</version>
+        </plugin>
       </plugins>
     </pluginManagement>
 


[4/6] syncope git commit: More robust ConnObjectKey handling

Posted by il...@apache.org.
More robust ConnObjectKey handling


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

Branch: refs/heads/master
Commit: ad7bb3225531b0382f3625fc6d4ed197c38325ce
Parents: 3d4f233
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Mon Nov 6 09:08:33 2017 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Mon Nov 6 13:52:18 2017 +0100

----------------------------------------------------------------------
 .../provisioning/java/MappingManagerImpl.java   | 24 ++++++++++----------
 1 file changed, 12 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/ad7bb322/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
index a19d37f..dada9f1 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
@@ -190,18 +190,18 @@ public class MappingManagerImpl implements MappingManager {
             }
         }
 
-        Attribute connObjectKeyExtAttr =
-                AttributeUtil.find(MappingUtils.getConnObjectKeyItem(provision).get().getExtAttrName(), attributes);
-        if (connObjectKeyExtAttr != null) {
-            attributes.remove(connObjectKeyExtAttr);
-            attributes.add(AttributeBuilder.build(
-                    MappingUtils.getConnObjectKeyItem(provision).get().getExtAttrName(), connObjectKey));
-        }
-        Name name = MappingUtils.evaluateNAME(any, provision, connObjectKey);
-        attributes.add(name);
-        if (connObjectKey != null && !connObjectKey.equals(name.getNameValue()) && connObjectKeyExtAttr == null) {
-            attributes.add(AttributeBuilder.build(
-                    MappingUtils.getConnObjectKeyItem(provision).get().getExtAttrName(), connObjectKey));
+        Optional<MappingItem> connObjectKeyItem = MappingUtils.getConnObjectKeyItem(provision);
+        if (connObjectKeyItem.isPresent()) {
+            Attribute connObjectKeyExtAttr = AttributeUtil.find(connObjectKeyItem.get().getExtAttrName(), attributes);
+            if (connObjectKeyExtAttr != null) {
+                attributes.remove(connObjectKeyExtAttr);
+                attributes.add(AttributeBuilder.build(connObjectKeyItem.get().getExtAttrName(), connObjectKey));
+            }
+            Name name = MappingUtils.evaluateNAME(any, provision, connObjectKey);
+            attributes.add(name);
+            if (connObjectKey != null && !connObjectKey.equals(name.getNameValue()) && connObjectKeyExtAttr == null) {
+                attributes.add(AttributeBuilder.build(connObjectKeyItem.get().getExtAttrName(), connObjectKey));
+            }
         }
 
         if (enable != null) {


[5/6] syncope git commit: White noise: javadocs cleanup

Posted by il...@apache.org.
White noise: javadocs cleanup


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

Branch: refs/heads/master
Commit: 9129ee05f76ed6cd525622731c215ce3a4b5d781
Parents: ad7bb32
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Mon Nov 6 12:50:43 2017 +0100
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Mon Nov 6 13:52:49 2017 +0100

----------------------------------------------------------------------
 .../syncope/core/persistence/api/search/SearchCondConverter.java  | 3 +--
 .../syncope/core/persistence/api/search/SearchCondVisitor.java    | 2 +-
 2 files changed, 2 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/9129ee05/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondConverter.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondConverter.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondConverter.java
index 1ec0f6f..257ba7f 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondConverter.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondConverter.java
@@ -35,12 +35,11 @@ import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
 public final class SearchCondConverter {
 
     /**
-     * Parses a FIQL expression into Syncope's <tt>SearchCond</tt>, using CXF's <tt>FiqlParser</tt>.
+     * Parses a FIQL expression into Syncope's {@link SearchCond}, using {@link SyncopeFiqlParser}.
      *
      * @param fiql FIQL string
      * @param realms optional realm to provide to {@link SearchCondVisitor}
      * @return {@link SearchCond} instance for given FIQL expression
-     * @see SyncopeFiqlParser
      */
     public static SearchCond convert(final String fiql, final String... realms) {
         SyncopeFiqlParser<SearchBean> parser = new SyncopeFiqlParser<>(

http://git-wip-us.apache.org/repos/asf/syncope/blob/9129ee05/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java
----------------------------------------------------------------------
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 18d5766..4c8cd01 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
@@ -47,7 +47,7 @@ import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond;
 import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond;
 
 /**
- * Converts CXF's <tt>SearchCondition</tt> into internal <tt>SearchCond</tt>.
+ * Visits CXF's {@link SearchBean} and produces {@link SearchCond}.
  */
 public class SearchCondVisitor extends AbstractSearchConditionVisitor<SearchBean, SearchCond> {