You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by an...@apache.org on 2023/10/11 13:43:32 UTC
[syncope] branch master updated: [SYNCOPE-1779] supporting underscore in search queries (#529)
This is an automated email from the ASF dual-hosted git repository.
andreapatricelli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git
The following commit(s) were added to refs/heads/master by this push:
new 0f250a2316 [SYNCOPE-1779] supporting underscore in search queries (#529)
0f250a2316 is described below
commit 0f250a2316c64c9f441574b50ef605ebbcb9d12f
Author: Andrea Patricelli <an...@apache.org>
AuthorDate: Wed Oct 11 14:39:20 2023 +0200
[SYNCOPE-1779] supporting underscore in search queries (#529)
* [SYNCOPE-1779] supporting underscore in search queries, with specific changes for Oracle DB
---
.../persistence/api/search/SearchCondVisitor.java | 3 +-
.../api/search/SearchCondConverterTest.java | 13 ++++++++
.../persistence/jpa/dao/OJPAJSONAnySearchDAO.java | 5 +++
.../core/persistence/jpa/dao/AbstractDAO.java | 22 ++++++++++++
.../core/persistence/jpa/dao/JPAAnySearchDAO.java | 5 +++
.../org/apache/syncope/fit/core/SearchITCase.java | 39 ++++++++++++++++++++++
6 files changed, 85 insertions(+), 2 deletions(-)
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 92de4a349e..6496411476 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
@@ -76,8 +76,7 @@ public class SearchCondVisitor extends AbstractSearchConditionVisitor<SearchBean
protected static String getValue(final SearchCondition<SearchBean> sc) {
String value = SearchUtils.toSqlWildcardString(
- URLDecoder.decode(sc.getStatement().getValue().toString(), StandardCharsets.UTF_8), false).
- replaceAll("\\\\_", "_");
+ URLDecoder.decode(sc.getStatement().getValue().toString(), StandardCharsets.UTF_8), false);
// see SYNCOPE-1321
if (TIMEZONE.matcher(value).matches()) {
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 eff4efd305..a98dc321c9 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
@@ -329,4 +329,17 @@ public class SearchCondConverterTest {
assertEquals(SearchCond.getLeaf(cond), SearchCondConverter.convert(VISITOR, fiql));
}
+
+ @Test
+ public void issueSYNCOPE1779() {
+ String fiql = new UserFiqlSearchConditionBuilder().is("username").equalToIgnoreCase("ros_*").query();
+ assertEquals("username=~ros_*", fiql);
+
+ AttrCond attrCond = new AnyCond(AttrCond.Type.ILIKE);
+ attrCond.setSchema("username");
+ attrCond.setExpression("ros\\_%");
+ SearchCond leaf = SearchCond.getLeaf(attrCond);
+
+ assertEquals(leaf, SearchCondConverter.convert(VISITOR, fiql));
+ }
}
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OJPAJSONAnySearchDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OJPAJSONAnySearchDAO.java
index ad4aaa9434..67216ee358 100644
--- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OJPAJSONAnySearchDAO.java
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OJPAJSONAnySearchDAO.java
@@ -244,6 +244,11 @@ public class OJPAJSONAnySearchDAO extends JPAAnySearchDAO {
query.append(lower ? "LOWER(" : "").
append('?').append(setParameter(parameters, value)).
append(lower ? ")" : "");
+ // workaround for Oracle DB adding explicit escaping string, to search
+ // for literal _ (underscore) (SYNCOPE-1779)
+ if (cond.getType() == AttrCond.Type.ILIKE || cond.getType() == AttrCond.Type.LIKE) {
+ query.append(" ESCAPE '\\' ");
+ }
}
}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractDAO.java
index 6f3863ccdb..8a529246e4 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractDAO.java
@@ -20,7 +20,14 @@ package org.apache.syncope.core.persistence.jpa.dao;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
+import java.util.Map;
import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import org.apache.openjpa.jdbc.meta.MappingRepository;
+import org.apache.openjpa.jdbc.sql.OracleDictionary;
+import org.apache.openjpa.persistence.OpenJPAEntityManagerFactory;
+import org.apache.openjpa.persistence.OpenJPAEntityManagerFactorySPI;
+import org.apache.openjpa.persistence.OpenJPAPersistence;
import org.apache.syncope.core.persistence.api.dao.DAO;
import org.apache.syncope.core.persistence.api.entity.Entity;
import org.apache.syncope.core.spring.ApplicationContextProvider;
@@ -33,6 +40,8 @@ public abstract class AbstractDAO<E extends Entity> implements DAO<E> {
protected static final Logger LOG = LoggerFactory.getLogger(DAO.class);
+ private static final Map<String, Boolean> IS_ORACLE = new ConcurrentHashMap<>();
+
protected EntityManagerFactory entityManagerFactory() {
return EntityManagerFactoryUtils.findEntityManagerFactory(
ApplicationContextProvider.getBeanFactory(), AuthContextUtils.getDomain());
@@ -53,4 +62,17 @@ public abstract class AbstractDAO<E extends Entity> implements DAO<E> {
public void detach(final E entity) {
entityManager().detach(entity);
}
+
+ protected boolean isOracle() {
+ Boolean isOracle = IS_ORACLE.get(AuthContextUtils.getDomain());
+ if (isOracle == null) {
+ OpenJPAEntityManagerFactory emf = OpenJPAPersistence.cast(entityManagerFactory());
+ OpenJPAEntityManagerFactorySPI emfspi = (OpenJPAEntityManagerFactorySPI) OpenJPAPersistence.cast(emf);
+ isOracle = ((MappingRepository) emfspi.getConfiguration()
+ .getMetaDataRepositoryInstance()).getDBDictionary() instanceof OracleDictionary;
+ IS_ORACLE.put(AuthContextUtils.getDomain(), isOracle);
+
+ }
+ return isOracle;
+ }
}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
index 622f8dd505..4e6d29aeb2 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAnySearchDAO.java
@@ -999,6 +999,11 @@ public class JPAAnySearchDAO extends AbstractAnySearchDAO {
} else {
query.append('?').append(setParameter(parameters, cond.getExpression()));
}
+ // workaround for Oracle DB adding explicit escaping string, to search
+ // for literal _ (underscore) (SYNCOPE-1779)
+ if (isOracle()) {
+ query.append(" ESCAPE '\\' ");
+ }
} else {
if (!(cond instanceof AnyCond)) {
query.append("' AND");
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 fe0eb5663d..bb13fe418f 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
@@ -875,4 +875,43 @@ public class SearchITCase extends AbstractITCase {
assertEquals(1, users.getResult().size());
assertEquals(user.getKey(), users.getResult().get(0).getKey());
}
+
+ @Test
+ public void issueSYNCOPE1779() {
+ // 1. create user with underscore
+ UserTO userWithUnderscore = createUser(UserITCase.getSample("syncope1779_test@syncope.apache.org")).getEntity();
+ // 2 create second user without underscore
+ createUser(UserITCase.getSample("syncope1779test@syncope.apache.org")).getEntity();
+
+ // 3. search for user
+ if (IS_ELASTICSEARCH_ENABLED) {
+ try {
+ Thread.sleep(2000);
+ } catch (InterruptedException ex) {
+ // ignore
+ }
+ }
+
+ // Search by username
+ PagedResult<UserTO> users = USER_SERVICE.search(new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM)
+ .fiql(SyncopeClient.getUserSearchConditionBuilder().is("username").equalTo("syncope1779_*")
+ .and().is("firstname").equalTo("syncope1779_*")
+ .and().is("userId").equalTo("syncope1779_*").query())
+ .build());
+ assertEquals(1, users.getResult().size());
+ assertEquals(userWithUnderscore.getKey(), users.getResult().get(0).getKey());
+ // Search also by attribute
+ users = USER_SERVICE.search(new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM)
+ .fiql(SyncopeClient.getUserSearchConditionBuilder().is("email").equalTo("syncope1779_*").query())
+ .build());
+ assertEquals(1, users.getResult().size());
+ assertEquals(userWithUnderscore.getKey(), users.getResult().get(0).getKey());
+ // search for both
+ users = USER_SERVICE.search(new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM)
+ .fiql(SyncopeClient.getUserSearchConditionBuilder().is("email").equalTo("syncope1779*").query())
+ .build());
+ assertEquals(2, users.getResult().size());
+
+ users.getResult().forEach(u -> USER_SERVICE.delete(u.getKey()));
+ }
}