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/10/12 12:34:35 UTC

[2/4] syncope git commit: [SYNCOPE-1223] Now allowing FIQL expressions with escaped , and ;

[SYNCOPE-1223] Now allowing FIQL expressions with escaped , and ;


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

Branch: refs/heads/master
Commit: 761d1045e5d0ce147271f1780b15d635117e056a
Parents: df35f9c
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Thu Oct 12 13:59:50 2017 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Thu Oct 12 14:27:18 2017 +0200

----------------------------------------------------------------------
 .../client/console/commons/AnyDataProvider.java |  63 +++++++---
 .../console/panels/AnyDirectoryPanel.java       |   3 +-
 .../console/panels/search/SearchUtils.java      | 126 +++++++++----------
 .../api/search/SearchCondConverter.java         |  21 ++--
 .../api/search/SearchCondVisitor.java           |  13 +-
 .../api/search/SearchCondConverterTest.java     |  11 ++
 .../rest/cxf/service/AbstractServiceImpl.java   |   2 +
 .../apache/syncope/fit/core/SearchITCase.java   |  22 ++++
 8 files changed, 169 insertions(+), 92 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/761d1045/client/console/src/main/java/org/apache/syncope/client/console/commons/AnyDataProvider.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/commons/AnyDataProvider.java b/client/console/src/main/java/org/apache/syncope/client/console/commons/AnyDataProvider.java
index 20ac7ff..8b3c67b 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/commons/AnyDataProvider.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/commons/AnyDataProvider.java
@@ -21,16 +21,25 @@ package org.apache.syncope.client.console.commons;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.pages.BasePage;
 import org.apache.syncope.client.console.rest.AbstractAnyRestClient;
 import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
 import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder;
 import org.apache.wicket.model.CompoundPropertyModel;
 import org.apache.wicket.model.IModel;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class AnyDataProvider<A extends AnyTO> extends DirectoryDataProvider<A> {
 
     private static final long serialVersionUID = 6267494272884913376L;
 
+    protected static final Logger LOG = LoggerFactory.getLogger(AnyDataProvider.class);
+
     private final SortableAnyProviderComparator<A> comparator;
 
     private final AbstractAnyRestClient<A, ?> restClient;
@@ -43,12 +52,15 @@ public class AnyDataProvider<A extends AnyTO> extends DirectoryDataProvider<A> {
 
     private final String type;
 
+    private final PageReference pageRef;
+
     public AnyDataProvider(
             final AbstractAnyRestClient<A, ?> restClient,
             final int paginatorRows,
             final boolean filtered,
             final String realm,
-            final String type) {
+            final String type,
+            final PageReference pageRef) {
 
         super(paginatorRows);
 
@@ -74,20 +86,29 @@ public class AnyDataProvider<A extends AnyTO> extends DirectoryDataProvider<A> {
 
         this.realm = realm;
         this.type = type;
+        this.pageRef = pageRef;
     }
 
     @Override
     public Iterator<A> iterator(final long first, final long count) {
-        List<A> result;
-
-        final int page = ((int) first / paginatorRows);
-
-        if (filtered) {
-            result = fiql == null
-                    ? Collections.<A>emptyList()
-                    : restClient.search(realm, fiql, (page < 0 ? 0 : page) + 1, paginatorRows, getSort(), type);
-        } else {
-            result = restClient.search(realm, null, (page < 0 ? 0 : page) + 1, paginatorRows, getSort(), type);
+        List<A> result = Collections.emptyList();
+
+        try {
+            final int page = ((int) first / paginatorRows);
+
+            if (filtered) {
+                result = fiql == null
+                        ? Collections.<A>emptyList()
+                        : restClient.search(realm, fiql, (page < 0 ? 0 : page) + 1, paginatorRows, getSort(), type);
+            } else {
+                result = restClient.search(realm, null, (page < 0 ? 0 : page) + 1, paginatorRows, getSort(), type);
+            }
+        } catch (Exception e) {
+            LOG.error("While searching with FIQL {}", fiql, e);
+            SyncopeConsoleSession.get().error(e.getMessage());
+
+            ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(
+                    RequestCycle.get().find(AjaxRequestTarget.class));
         }
 
         Collections.sort(result, comparator);
@@ -96,12 +117,20 @@ public class AnyDataProvider<A extends AnyTO> extends DirectoryDataProvider<A> {
 
     @Override
     public long size() {
-        long result;
-
-        if (filtered) {
-            result = fiql == null ? 0 : restClient.searchCount(realm, fiql, type);
-        } else {
-            result = restClient.searchCount(realm, null, type);
+        long result = 0;
+
+        try {
+            if (filtered) {
+                result = fiql == null ? 0 : restClient.searchCount(realm, fiql, type);
+            } else {
+                result = restClient.searchCount(realm, null, type);
+            }
+        } catch (Exception e) {
+            LOG.error("While requesting for size() with FIQL {}", fiql, e);
+            SyncopeConsoleSession.get().error(e.getMessage());
+
+            ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(
+                    RequestCycle.get().find(AjaxRequestTarget.class));
         }
 
         return result;

http://git-wip-us.apache.org/repos/asf/syncope/blob/761d1045/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyDirectoryPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyDirectoryPanel.java
index 828a698..da58347 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyDirectoryPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/AnyDirectoryPanel.java
@@ -211,8 +211,7 @@ public abstract class AnyDirectoryPanel<A extends AnyTO, E extends AbstractAnyRe
 
     @Override
     protected AnyDataProvider<A> dataProvider() {
-        final AnyDataProvider<A> dp = new AnyDataProvider<>(restClient, rows, filtered, realm, type);
-        return dp.setFIQL(this.fiql);
+        return new AnyDataProvider<>(restClient, rows, filtered, realm, type, pageRef).setFIQL(this.fiql);
     }
 
     public void search(final String fiql, final AjaxRequestTarget target) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/761d1045/client/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchUtils.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchUtils.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchUtils.java
index d7f4e1f..68ac17f 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchUtils.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/search/SearchUtils.java
@@ -55,48 +55,46 @@ public final class SearchUtils implements Serializable {
     }
 
     public static Map<String, List<SearchClause>> getSearchClauses(final Map<String, String> fiql) {
-        final Map<String, List<SearchClause>> res = new HashMap<>();
-        if (fiql != null && !fiql.isEmpty()) {
-            for (Map.Entry<String, String> entry : fiql.entrySet()) {
-                res.put(entry.getKey(), getSearchClauses(
-                        entry.getValue().replaceAll(getTypeConditionPattern(entry.getKey()).pattern(), "")));
-            }
+        Map<String, List<SearchClause>> clauses = new HashMap<>();
+        for (Map.Entry<String, String> entry : fiql.entrySet()) {
+            clauses.put(entry.getKey(), getSearchClauses(
+                    entry.getValue().replaceAll(getTypeConditionPattern(entry.getKey()).pattern(), "")));
         }
-        return res;
+        return clauses;
     }
 
     public static List<SearchClause> getSearchClauses(final String fiql) {
-        final List<SearchClause> res = new ArrayList<>();
+        List<SearchClause> clauses = new ArrayList<>();
         if (StringUtils.isNotBlank(fiql)) {
             try {
                 SyncopeFiqlParser<SearchBean> fiqlParser = new SyncopeFiqlParser<>(
                         SearchBean.class, AbstractFiqlSearchConditionBuilder.CONTEXTUAL_PROPERTIES);
-                res.addAll(getSearchClauses(fiqlParser.parse(fiql)));
+                clauses.addAll(getSearchClauses(fiqlParser.parse(fiql)));
             } catch (Exception e) {
                 LOG.error("Unparseable FIQL expression '{}'", fiql, e);
             }
         }
-        return res;
+        return clauses;
     }
 
     private static List<SearchClause> getSearchClauses(final SearchCondition<SearchBean> sc) {
-        List<SearchClause> res = new ArrayList<>();
+        List<SearchClause> clauses = new ArrayList<>();
 
         if (sc.getStatement() == null) {
-            res.addAll(getCompoundSearchClause(sc));
+            clauses.addAll(getCompoundSearchClauses(sc));
         } else {
-            res.add(getPrimitiveSearchClause(sc));
+            clauses.add(getPrimitiveSearchClause(sc));
         }
 
-        return res;
+        return clauses;
     }
 
-    private static List<SearchClause> getCompoundSearchClause(final SearchCondition<SearchBean> sc) {
-        List<SearchClause> res = new ArrayList<>();
+    private static List<SearchClause> getCompoundSearchClauses(final SearchCondition<SearchBean> sc) {
+        List<SearchClause> clauses = new ArrayList<>();
 
         for (SearchCondition<SearchBean> searchCondition : sc.getSearchConditions()) {
             if (searchCondition.getStatement() == null) {
-                res.addAll(getCompoundSearchClause(searchCondition));
+                clauses.addAll(getCompoundSearchClauses(searchCondition));
             } else {
                 SearchClause clause = getPrimitiveSearchClause(searchCondition);
                 if (sc.getConditionType() == ConditionType.AND) {
@@ -105,43 +103,43 @@ public final class SearchUtils implements Serializable {
                 if (sc.getConditionType() == ConditionType.OR) {
                     clause.setOperator(SearchClause.Operator.OR);
                 }
-                res.add(clause);
+                clauses.add(clause);
             }
         }
 
-        return res;
+        return clauses;
     }
 
     private static SearchClause getPrimitiveSearchClause(final SearchCondition<SearchBean> sc) {
-        SearchClause res = new SearchClause();
+        SearchClause clause = new SearchClause();
 
         String property = sc.getCondition().getKeySet().iterator().next();
-        res.setProperty(property);
-        String value = sc.getCondition().get(property);
-        res.setValue(value);
+        clause.setProperty(property);
+        String value = sc.getCondition().get(property).replace("%252C", ",").replace("%253B", ";");
+        clause.setValue(value);
 
         LOG.debug("Condition: " + sc.getCondition());
 
         if (SpecialAttr.ROLES.toString().equals(property)) {
-            res.setType(SearchClause.Type.ROLE_MEMBERSHIP);
-            res.setProperty(value);
+            clause.setType(SearchClause.Type.ROLE_MEMBERSHIP);
+            clause.setProperty(value);
         } else if (SpecialAttr.RELATIONSHIPS.toString().equals(property)) {
-            res.setType(SearchClause.Type.RELATIONSHIP);
-            res.setProperty(value);
+            clause.setType(SearchClause.Type.RELATIONSHIP);
+            clause.setProperty(value);
         } else if (SpecialAttr.RELATIONSHIP_TYPES.toString().equals(property)) {
-            res.setType(SearchClause.Type.RELATIONSHIP);
-            res.setProperty(value);
+            clause.setType(SearchClause.Type.RELATIONSHIP);
+            clause.setProperty(value);
         } else if (SpecialAttr.GROUPS.toString().equals(property)) {
-            res.setType(SearchClause.Type.GROUP_MEMBERSHIP);
-            res.setProperty(value);
+            clause.setType(SearchClause.Type.GROUP_MEMBERSHIP);
+            clause.setProperty(value);
         } else if (SpecialAttr.RESOURCES.toString().equals(property)) {
-            res.setType(SearchClause.Type.RESOURCE);
-            res.setProperty(value);
+            clause.setType(SearchClause.Type.RESOURCE);
+            clause.setProperty(value);
         } else if (SpecialAttr.MEMBER.toString().equals(property)) {
-            res.setType(SearchClause.Type.GROUP_MEMBER);
-            res.setProperty(value);
+            clause.setType(SearchClause.Type.GROUP_MEMBER);
+            clause.setProperty(value);
         } else {
-            res.setType(SearchClause.Type.ATTRIBUTE);
+            clause.setType(SearchClause.Type.ATTRIBUTE);
         }
 
         ConditionType ct = sc.getConditionType();
@@ -156,45 +154,45 @@ public final class SearchUtils implements Serializable {
         switch (ct) {
             case EQUALS:
                 if (SpecialAttr.RELATIONSHIP_TYPES.toString().equals(property)) {
-                    res.setComparator(SpecialAttr.NULL.toString().equals(value)
+                    clause.setComparator(SpecialAttr.NULL.toString().equals(value)
                             ? SearchClause.Comparator.EQUALS : SearchClause.Comparator.IS_NULL);
                 } else {
-                    res.setComparator(SpecialAttr.NULL.toString().equals(value)
+                    clause.setComparator(SpecialAttr.NULL.toString().equals(value)
                             ? SearchClause.Comparator.IS_NULL : SearchClause.Comparator.EQUALS);
                 }
                 break;
 
             case NOT_EQUALS:
                 if (SpecialAttr.RELATIONSHIP_TYPES.toString().equals(property)) {
-                    res.setComparator(SpecialAttr.NULL.toString().equals(value)
+                    clause.setComparator(SpecialAttr.NULL.toString().equals(value)
                             ? SearchClause.Comparator.NOT_EQUALS : SearchClause.Comparator.IS_NOT_NULL);
                 } else {
-                    res.setComparator(SpecialAttr.NULL.toString().equals(value)
+                    clause.setComparator(SpecialAttr.NULL.toString().equals(value)
                             ? SearchClause.Comparator.IS_NOT_NULL : SearchClause.Comparator.NOT_EQUALS);
                 }
                 break;
 
             case GREATER_OR_EQUALS:
-                res.setComparator(SearchClause.Comparator.GREATER_OR_EQUALS);
+                clause.setComparator(SearchClause.Comparator.GREATER_OR_EQUALS);
                 break;
 
             case GREATER_THAN:
-                res.setComparator(SearchClause.Comparator.GREATER_THAN);
+                clause.setComparator(SearchClause.Comparator.GREATER_THAN);
                 break;
 
             case LESS_OR_EQUALS:
-                res.setComparator(SearchClause.Comparator.LESS_OR_EQUALS);
+                clause.setComparator(SearchClause.Comparator.LESS_OR_EQUALS);
                 break;
 
             case LESS_THAN:
-                res.setComparator(SearchClause.Comparator.LESS_THAN);
+                clause.setComparator(SearchClause.Comparator.LESS_THAN);
                 break;
 
             default:
                 break;
         }
 
-        return res;
+        return clause;
     }
 
     public static String buildFIQL(final List<SearchClause> clauses, final AbstractFiqlSearchConditionBuilder builder) {
@@ -216,17 +214,19 @@ public final class SearchUtils implements Serializable {
             prevCondition = condition;
 
             if (clause.getType() != null) {
+                String value = clause.getValue() == null
+                        ? null
+                        : clause.getValue().replace(",", "%252C").replace(";", "%253B");
+
                 switch (clause.getType()) {
                     case GROUP_MEMBER:
                         switch (clause.getComparator()) {
                             case EQUALS:
-                                condition = ((GroupFiqlSearchConditionBuilder) builder).
-                                        withMembers(clause.getValue());
+                                condition = ((GroupFiqlSearchConditionBuilder) builder).withMembers(value);
                                 break;
 
                             case NOT_EQUALS:
-                                condition = ((GroupFiqlSearchConditionBuilder) builder).
-                                        withoutMembers(clause.getValue());
+                                condition = ((GroupFiqlSearchConditionBuilder) builder).withoutMembers(value);
                                 break;
 
                             default:
@@ -275,35 +275,35 @@ public final class SearchUtils implements Serializable {
 
                                 case LESS_THAN:
                                     condition = isLong
-                                            ? property.lessThan(NumberUtils.toLong(clause.getValue()))
-                                            : property.lexicalBefore(clause.getValue());
+                                            ? property.lessThan(NumberUtils.toLong(value))
+                                            : property.lexicalBefore(value);
                                     break;
 
                                 case LESS_OR_EQUALS:
                                     condition = isLong
-                                            ? property.lessOrEqualTo(NumberUtils.toLong(clause.getValue()))
-                                            : property.lexicalNotAfter(clause.getValue());
+                                            ? property.lessOrEqualTo(NumberUtils.toLong(value))
+                                            : property.lexicalNotAfter(value);
                                     break;
 
                                 case GREATER_THAN:
                                     condition = isLong
-                                            ? property.greaterThan(NumberUtils.toLong(clause.getValue()))
-                                            : property.lexicalAfter(clause.getValue());
+                                            ? property.greaterThan(NumberUtils.toLong(value))
+                                            : property.lexicalAfter(value);
                                     break;
 
                                 case GREATER_OR_EQUALS:
                                     condition = isLong
-                                            ? property.greaterOrEqualTo(NumberUtils.toLong(clause.getValue()))
-                                            : property.lexicalNotBefore(clause.getValue());
+                                            ? property.greaterOrEqualTo(NumberUtils.toLong(value))
+                                            : property.lexicalNotBefore(value);
                                     break;
 
                                 case NOT_EQUALS:
-                                    condition = property.notEqualTolIgnoreCase(clause.getValue());
+                                    condition = property.notEqualTolIgnoreCase(value);
                                     break;
 
                                 case EQUALS:
                                 default:
-                                    condition = property.equalToIgnoreCase(clause.getValue());
+                                    condition = property.equalToIgnoreCase(value);
                                     break;
                             }
                         }
@@ -340,11 +340,11 @@ public final class SearchUtils implements Serializable {
                                         break;
                                     case EQUALS:
                                         condition = ((UserFiqlSearchConditionBuilder) builder).
-                                                inRelationships(clause.getValue());
+                                                inRelationships(value);
                                         break;
                                     case NOT_EQUALS:
                                         condition = ((UserFiqlSearchConditionBuilder) builder).
-                                                notInRelationships(clause.getValue());
+                                                notInRelationships(value);
                                         break;
                                     default:
                                         break;
@@ -361,11 +361,11 @@ public final class SearchUtils implements Serializable {
                                         break;
                                     case EQUALS:
                                         condition = ((AnyObjectFiqlSearchConditionBuilder) builder).
-                                                inRelationships(clause.getValue());
+                                                inRelationships(value);
                                         break;
                                     case NOT_EQUALS:
                                         condition = ((AnyObjectFiqlSearchConditionBuilder) builder).
-                                                notInRelationships(clause.getValue());
+                                                notInRelationships(value);
                                         break;
                                     default:
                                         break;

http://git-wip-us.apache.org/repos/asf/syncope/blob/761d1045/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 f07453f..1ec0f6f 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
@@ -18,8 +18,11 @@
  */
 package org.apache.syncope.core.persistence.api.search;
 
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
 import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.apache.cxf.jaxrs.ext.search.SearchBean;
+import org.apache.cxf.jaxrs.ext.search.SearchCondition;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.search.AbstractFiqlSearchConditionBuilder;
 import org.apache.syncope.common.lib.search.SyncopeFiqlParser;
@@ -34,25 +37,27 @@ public final class SearchCondConverter {
     /**
      * Parses a FIQL expression into Syncope's <tt>SearchCond</tt>, using CXF's <tt>FiqlParser</tt>.
      *
-     * @param fiqlExpression FIQL string
+     * @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 fiqlExpression, final String... realms) {
-        SyncopeFiqlParser<SearchBean> fiqlParser = new SyncopeFiqlParser<>(
+    public static SearchCond convert(final String fiql, final String... realms) {
+        SyncopeFiqlParser<SearchBean> parser = new SyncopeFiqlParser<>(
                 SearchBean.class, AbstractFiqlSearchConditionBuilder.CONTEXTUAL_PROPERTIES);
 
         try {
-            SearchCondVisitor searchCondVisitor = new SearchCondVisitor();
+            SearchCondVisitor visitor = new SearchCondVisitor();
             if (realms != null && realms.length > 0) {
-                searchCondVisitor.setRealm(realms[0]);
+                visitor.setRealm(realms[0]);
             }
-            searchCondVisitor.visit(fiqlParser.parse(fiqlExpression));
-            return searchCondVisitor.getQuery();
+            SearchCondition<SearchBean> sc = parser.parse(URLDecoder.decode(fiql, StandardCharsets.UTF_8.name()));
+            sc.accept(visitor);
+
+            return visitor.getQuery();
         } catch (Exception e) {
             SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSearchExpression);
-            sce.getElements().add(fiqlExpression);
+            sce.getElements().add(fiql);
             sce.getElements().add(ExceptionUtils.getRootCauseMessage(e));
             throw sce;
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/761d1045/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 2af6f25..18d5766 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
@@ -18,6 +18,9 @@
  */
 package org.apache.syncope.core.persistence.api.search;
 
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
@@ -73,8 +76,14 @@ public class SearchCondVisitor extends AbstractSearchConditionVisitor<SearchBean
         String name = getRealPropertyName(sc.getStatement().getProperty());
         Optional<SpecialAttr> specialAttrName = SpecialAttr.fromString(name);
 
-        String value = SearchUtils.toSqlWildcardString(sc.getStatement().getValue().toString(), false).
-                replaceAll("\\\\_", "_");
+        String value = null;
+        try {
+            value = SearchUtils.toSqlWildcardString(
+                    URLDecoder.decode(sc.getStatement().getValue().toString(), StandardCharsets.UTF_8.name()), false).
+                    replaceAll("\\\\_", "_");
+        } catch (UnsupportedEncodingException e) {
+            throw new IllegalArgumentException("While decoding " + sc.getStatement().getValue(), e);
+        }
         Optional<SpecialAttr> specialAttrValue = SpecialAttr.fromString(value);
 
         AttributeCond attributeCond = createAttributeCond(name);

http://git-wip-us.apache.org/repos/asf/syncope/blob/761d1045/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java
index 6d5981e..10e229f 100644
--- a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java
+++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java
@@ -305,4 +305,15 @@ public class SearchCondConverterTest {
         assertEquals(orCond, SearchCondConverter.convert(fiql));
     }
 
+    @Test
+    public void issueSYNCOPE1223() {
+        String fiql = new UserFiqlSearchConditionBuilder().is("ctype").equalTo("ou=sample%252Co=isp").query();
+
+        AttributeCond cond = new AttributeCond(AttributeCond.Type.EQ);
+        cond.setSchema("ctype");
+        cond.setExpression("ou=sample,o=isp");
+
+        assertEquals(SearchCond.getLeafCond(cond), SearchCondConverter.convert(fiql));
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/761d1045/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java
index 4ca01dc..a040960 100644
--- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractServiceImpl.java
@@ -30,6 +30,7 @@ import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.apache.cxf.jaxrs.ext.MessageContext;
 import org.apache.cxf.jaxrs.ext.search.SearchBean;
 import org.apache.cxf.jaxrs.ext.search.SearchCondition;
@@ -161,6 +162,7 @@ abstract class AbstractServiceImpl implements JAXRSService {
 
             SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSearchExpression);
             sce.getElements().add(fiql);
+            sce.getElements().add(ExceptionUtils.getRootCauseMessage(e));
             throw sce;
         }
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/761d1045/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java
index e8002a6..0677ad7 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java
@@ -28,7 +28,9 @@ import javax.ws.rs.core.Response;
 import org.apache.syncope.client.lib.SyncopeClient;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.patch.AnyObjectPatch;
+import org.apache.syncope.common.lib.patch.AttrPatch;
 import org.apache.syncope.common.lib.patch.MembershipPatch;
+import org.apache.syncope.common.lib.patch.UserPatch;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.common.lib.to.AnyTypeTO;
 import org.apache.syncope.common.lib.to.PagedResult;
@@ -471,4 +473,24 @@ public class SearchITCase extends AbstractITCase {
                         build());
         assertNotEquals(0, users.getTotalCount());
     }
+
+    @Test
+    public void issueSYNCOPE1223() {
+        UserPatch patch = new UserPatch();
+        patch.setKey("vivaldi");
+        patch.getPlainAttrs().add(new AttrPatch.Builder().attrTO(attrTO("ctype", "ou=sample,o=isp")).build());
+        userService.update(patch);
+
+        try {
+            PagedResult<UserTO> users = userService.search(new AnyQuery.Builder().fiql(
+                    SyncopeClient.getUserSearchConditionBuilder().is("ctype").equalTo("ou=sample%252Co=isp").query()).
+                    build());
+            assertEquals(1, users.getTotalCount());
+            assertEquals("vivaldi", users.getResult().get(0).getUsername());
+        } finally {
+            patch.getPlainAttrs().clear();
+            patch.getPlainAttrs().add(new AttrPatch.Builder().attrTO(attrTO("ctype", "F")).build());
+            userService.update(patch);
+        }
+    }
 }