This is an automated email from the ASF dual-hosted git repository.
ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git
The following commit(s) were added to refs/heads/master by this push:
new 10fc62dc38 [SYNCOPE-1871] Allow to search for Realms from multiple
bases (#1041)
10fc62dc38 is described below
commit 10fc62dc38ba220e08f881933ff8ff9d6aa70524
Author: Francesco Chicchiriccò <[email protected]>
AuthorDate: Tue Apr 1 16:46:42 2025 +0200
[SYNCOPE-1871] Allow to search for Realms from multiple bases (#1041)
---
.../clientapps/ClientAppModalPanelBuilder.java | 2 +-
.../client/console/status/ReconTaskPanel.java | 2 +-
.../wizards/resources/ConnectorDetailsPanel.java | 2 +-
.../client/console/SyncopeWebApplication.java | 2 +-
.../client/console/commons/RealmsUtils.java | 15 +++++----
.../client/console/panels/RealmChoicePanel.java | 2 +-
.../console/tasks/SchedTaskWizardBuilder.java | 2 +-
.../client/console/wizards/any/Details.java | 2 +-
.../console/wizards/role/RoleWizardBuilder.java | 2 +-
.../syncope/common/rest/api/beans/RealmQuery.java | 39 ++++++++++++++++------
.../org/apache/syncope/core/logic/RealmLogic.java | 23 +++++++++----
.../core/rest/cxf/service/RealmServiceImpl.java | 2 +-
.../core/persistence/api/dao/RealmSearchDAO.java | 5 +++
.../persistence/jpa/dao/JPARealmSearchDAO.java | 34 +++++++++++++------
.../core/persistence/jpa/inner/RealmTest.java | 7 ++++
.../persistence/neo4j/dao/Neo4jRealmSearchDAO.java | 39 +++++++++++++++-------
.../core/persistence/neo4j/inner/RealmTest.java | 7 ++++
.../dao/ElasticsearchRealmSearchDAO.java | 36 ++++++++++++++------
.../opensearch/dao/OpenSearchRealmSearchDAO.java | 37 +++++++++++++-------
.../syncope/fit/core/reference/TestCommand.java | 3 +-
.../org/apache/syncope/fit/core/RealmITCase.java | 12 +++++++
21 files changed, 197 insertions(+), 78 deletions(-)
diff --git
a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java
b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java
index 2c1322d906..4694247dc7 100644
---
a/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java
+++
b/client/am/console/src/main/java/org/apache/syncope/client/console/clientapps/ClientAppModalPanelBuilder.java
@@ -192,7 +192,7 @@ public class ClientAppModalPanelBuilder<T extends
ClientAppTO> extends AbstractM
@Override
protected Iterator<String> getChoices(final String input) {
return realmRestClient.search(fullRealmsTree
- ? RealmsUtils.buildRootQuery()
+ ? RealmsUtils.buildBaseQuery()
:
RealmsUtils.buildKeywordQuery(input)).getResult().stream().
map(RealmTO::getFullPath).iterator();
}
diff --git
a/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java
b/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java
index 5503421af1..9851476317 100644
---
a/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java
+++
b/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java
@@ -140,7 +140,7 @@ public class ReconTaskPanel extends
MultilevelPanel.SecondLevel {
protected Iterator<String> getChoices(final String input) {
return (RealmsUtils.checkInput(input)
? (realmRestClient.search(fullRealmsTree
- ? RealmsUtils.buildRootQuery()
+ ? RealmsUtils.buildBaseQuery()
:
RealmsUtils.buildKeywordQuery(input)).getResult())
: List.<RealmTO>of()).stream().
map(RealmTO::getFullPath).iterator();
diff --git
a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorDetailsPanel.java
b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorDetailsPanel.java
index b45698fb47..1918fa55d6 100644
---
a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorDetailsPanel.java
+++
b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorDetailsPanel.java
@@ -68,7 +68,7 @@ public class ConnectorDetailsPanel extends WizardStep {
protected Iterator<String> getChoices(final String input) {
return (RealmsUtils.checkInput(input)
? (realmRestClient.search(fullRealmsTree
- ? RealmsUtils.buildRootQuery()
+ ? RealmsUtils.buildBaseQuery()
:
RealmsUtils.buildKeywordQuery(input)).getResult())
: List.<RealmTO>of()).stream().
map(RealmTO::getFullPath).iterator();
diff --git
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeWebApplication.java
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeWebApplication.java
index dd111445c3..b4003dd6d1 100644
---
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeWebApplication.java
+++
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeWebApplication.java
@@ -316,7 +316,7 @@ public class SyncopeWebApplication extends
WicketBootSecuredWebApplication {
return false;
}
- RealmQuery query = RealmsUtils.buildRootQuery();
+ RealmQuery query = RealmsUtils.buildBaseQuery();
query.setPage(1);
query.setSize(0);
return restClient.search(query).getTotalCount() <
props.getRealmsFullTreeThreshold();
diff --git
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/RealmsUtils.java
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/RealmsUtils.java
index f22df570c3..c578e59425 100644
---
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/RealmsUtils.java
+++
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/RealmsUtils.java
@@ -18,6 +18,7 @@
*/
package org.apache.syncope.client.console.commons;
+import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.client.console.SyncopeConsoleSession;
import org.apache.syncope.common.lib.SyncopeConstants;
@@ -41,12 +42,14 @@ public final class RealmsUtils {
return new RealmQuery.Builder().keyword(input.contains("*") ? input :
"*" + input + "*").build();
}
- public static RealmQuery buildRootQuery() {
- String base =
SyncopeConsoleSession.get().getSearchableRealms().isEmpty()
- ||
SyncopeConsoleSession.get().getSearchableRealms().contains(SyncopeConstants.ROOT_REALM)
- ? SyncopeConstants.ROOT_REALM
- :
getFullPath(SyncopeConsoleSession.get().getSearchableRealms().getFirst());
- return new RealmQuery.Builder().base(base).build();
+ public static RealmQuery buildBaseQuery() {
+ List<String> realms =
SyncopeConsoleSession.get().getSearchableRealms();
+
+ if (realms.isEmpty() || realms.contains(SyncopeConstants.ROOT_REALM)) {
+ return new
RealmQuery.Builder().base(SyncopeConstants.ROOT_REALM).build();
+ }
+
+ return new
RealmQuery.Builder().bases(realms.stream().map(RealmsUtils::getFullPath).toList()).build();
}
private RealmsUtils() {
diff --git
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/RealmChoicePanel.java
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/RealmChoicePanel.java
index def5538df9..401fe87677 100644
---
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/RealmChoicePanel.java
+++
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/RealmChoicePanel.java
@@ -454,7 +454,7 @@ public class RealmChoicePanel extends Panel {
protected Map<String, Pair<RealmTO, List<RealmTO>>> reloadRealmParentMap()
{
List<RealmTO> realmsToList = realmRestClient.search(fullRealmsTree
- ? RealmsUtils.buildRootQuery()
+ ? RealmsUtils.buildBaseQuery()
: RealmsUtils.buildKeywordQuery(searchQuery)).getResult();
return reloadRealmParentMap(realmsToList.stream().
diff --git
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java
index dbf73c9eca..370aa053e1 100644
---
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java
+++
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java
@@ -123,7 +123,7 @@ public class SchedTaskWizardBuilder<T extends SchedTaskTO>
extends BaseAjaxWizar
protected List<String> searchRealms(final String realmQuery) {
return realmRestClient.search(fullRealmsTree
- ? RealmsUtils.buildRootQuery()
+ ? RealmsUtils.buildBaseQuery()
: RealmsUtils.buildKeywordQuery(realmQuery)).
getResult().stream().map(RealmTO::getFullPath).collect(Collectors.toList());
}
diff --git
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Details.java
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Details.java
index c2c09a2415..2cf541b1c0 100644
---
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Details.java
+++
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/any/Details.java
@@ -95,7 +95,7 @@ public class Details<T extends AnyTO> extends WizardStep {
return (pageRef.getPage() instanceof Realms
?
getRealmsFromLinks(Realms.class.cast(pageRef.getPage()).getRealmChoicePanel().getLinks())
: (fullRealmsTree
- ?
realmRestClient.search(RealmsUtils.buildRootQuery())
+ ?
realmRestClient.search(RealmsUtils.buildBaseQuery())
:
realmRestClient.search(RealmsUtils.buildKeywordQuery(input))).getResult()).
stream().map(RealmTO::getFullPath).
filter(fullPath -> authRealms.stream().anyMatch(
diff --git
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleWizardBuilder.java
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleWizardBuilder.java
index bab9d537bb..28abc47ef3 100644
---
a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleWizardBuilder.java
+++
b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wizards/role/RoleWizardBuilder.java
@@ -189,7 +189,7 @@ public class RoleWizardBuilder extends
BaseAjaxWizardBuilder<RoleWrapper> {
@Override
protected Iterator<String> getChoices(final String input) {
return realmRestClient.search(fullRealmsTree
- ? RealmsUtils.buildRootQuery()
+ ? RealmsUtils.buildBaseQuery()
:
RealmsUtils.buildKeywordQuery(input)).getResult().stream().
map(RealmTO::getFullPath).iterator();
}
diff --git
a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/RealmQuery.java
b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/RealmQuery.java
index 0858b35680..badd154610 100644
---
a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/RealmQuery.java
+++
b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/RealmQuery.java
@@ -19,6 +19,12 @@
package org.apache.syncope.common.rest.api.beans;
import jakarta.ws.rs.QueryParam;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
@@ -38,15 +44,28 @@ public class RealmQuery extends AbstractQuery {
return this;
}
- public Builder base(final String base) {
- getInstance().setBase(base);
+ public Builder base(final String... bases) {
+ if (bases != null) {
+ Set<String> b =
Optional.ofNullable(getInstance().getBases()).orElseGet(HashSet::new);
+ b.addAll(Stream.of(bases).collect(Collectors.toSet()));
+ getInstance().setBases(b);
+ }
+ return this;
+ }
+
+ public Builder bases(final Collection<String> bases) {
+ if (bases != null) {
+ Set<String> b =
Optional.ofNullable(getInstance().getBases()).orElseGet(HashSet::new);
+ b.addAll(bases);
+ getInstance().setBases(b);
+ }
return this;
}
}
private String keyword;
- private String base;
+ private Set<String> bases;
public String getKeyword() {
return keyword;
@@ -57,13 +76,13 @@ public class RealmQuery extends AbstractQuery {
this.keyword = keyword;
}
- public String getBase() {
- return base;
+ public Set<String> getBases() {
+ return bases;
}
- @QueryParam("base")
- public void setBase(final String base) {
- this.base = base;
+ @QueryParam("bases")
+ public void setBases(final Set<String> bases) {
+ this.bases = bases;
}
@Override
@@ -81,7 +100,7 @@ public class RealmQuery extends AbstractQuery {
return new EqualsBuilder().
appendSuper(super.equals(obj)).
append(keyword, other.keyword).
- append(base, other.base).
+ append(bases, other.bases).
build();
}
@@ -90,7 +109,7 @@ public class RealmQuery extends AbstractQuery {
return new HashCodeBuilder().
appendSuper(super.hashCode()).
append(keyword).
- append(base).
+ append(bases).
build();
}
}
diff --git
a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java
b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java
index 68bcedbf1f..e2f6822cfa 100644
---
a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java
+++
b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java
@@ -20,6 +20,7 @@ package org.apache.syncope.core.logic;
import java.lang.reflect.Method;
import java.util.Comparator;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -27,6 +28,7 @@ import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.to.ProvisioningResult;
import org.apache.syncope.common.lib.to.RealmTO;
import org.apache.syncope.common.lib.types.AnyTypeKind;
@@ -61,6 +63,7 @@ import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
public class RealmLogic extends AbstractTransactionalLogic<RealmTO> {
@@ -120,18 +123,24 @@ public class RealmLogic extends
AbstractTransactionalLogic<RealmTO> {
@Transactional(readOnly = true)
public Page<RealmTO> search(
final String keyword,
- final String base,
+ final Set<String> bases,
final Pageable pageable) {
- Realm baseRealm = base == null
- ? realmDAO.getRoot()
- : realmSearchDAO.findByFullPath(base).orElseThrow(() -> new
NotFoundException("Realm " + base));
+ Set<String> baseRealms = new HashSet<>();
+ if (CollectionUtils.isEmpty(bases)) {
+ baseRealms.add(SyncopeConstants.ROOT_REALM);
+ } else {
+ for (String base : bases) {
+
baseRealms.add(realmSearchDAO.findByFullPath(base).map(Realm::getFullPath).
+ orElseThrow(() -> new NotFoundException("Realm " +
base)));
+ }
+ }
- long count = realmSearchDAO.countDescendants(baseRealm.getFullPath(),
keyword);
+ long count = realmSearchDAO.countDescendants(baseRealms, keyword);
Set<String> authorizations = AuthContextUtils.getAuthorizations().
- getOrDefault(IdRepoEntitlement.REALM_SEARCH, Set.of());
- List<RealmTO> result =
realmSearchDAO.findDescendants(baseRealm.getFullPath(), keyword,
pageable).stream().
+ getOrDefault(IdRepoEntitlement.REALM_SEARCH, Set.of());
+ List<RealmTO> result = realmSearchDAO.findDescendants(baseRealms,
keyword, pageable).stream().
map(realm -> binder.getRealmTO(
realm, authorizations.stream().
anyMatch(auth ->
realm.getFullPath().startsWith(auth)))).
diff --git
a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/RealmServiceImpl.java
b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/RealmServiceImpl.java
index 44fb4d8b0f..1bf32e6086 100644
---
a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/RealmServiceImpl.java
+++
b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/RealmServiceImpl.java
@@ -44,7 +44,7 @@ public class RealmServiceImpl extends AbstractService
implements RealmService {
public PagedResult<RealmTO> search(final RealmQuery query) {
Page<RealmTO> result = logic.search(
Optional.ofNullable(query.getKeyword()).map(k ->
k.replace('*', '%')).orElse(null),
- query.getBase(),
+ query.getBases(),
pageable(query));
return buildPagedResult(result);
}
diff --git
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmSearchDAO.java
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmSearchDAO.java
index 9c968c0bf7..3b3a740b5f 100644
---
a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmSearchDAO.java
+++
b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/RealmSearchDAO.java
@@ -21,6 +21,7 @@ package org.apache.syncope.core.persistence.api.dao;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import org.apache.syncope.core.persistence.api.entity.Realm;
import org.springframework.data.domain.Pageable;
@@ -34,8 +35,12 @@ public interface RealmSearchDAO {
long countDescendants(String base, String keyword);
+ long countDescendants(Set<String> bases, String keyword);
+
List<Realm> findDescendants(String base, String keyword, Pageable
pageable);
+ List<Realm> findDescendants(Set<String> bases, String keyword, Pageable
pageable);
+
List<String> findDescendants(String base, String prefix);
default void findAncestors(final List<Realm> result, final Realm realm) {
diff --git
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmSearchDAO.java
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmSearchDAO.java
index 461f393384..1124660fc9 100644
---
a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmSearchDAO.java
+++
b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmSearchDAO.java
@@ -25,6 +25,8 @@ import jakarta.persistence.TypedQuery;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.core.persistence.api.dao.MalformedPathException;
@@ -46,18 +48,20 @@ public class JPARealmSearchDAO implements RealmSearchDAO {
return parameters.size();
}
- protected static StringBuilder buildDescendantQuery(
- final String base,
+ protected static StringBuilder buildDescendantsQuery(
+ final Set<String> bases,
final String keyword,
final List<Object> parameters) {
+ String basesClause = bases.stream().
+ map(base -> "e.fullPath=?" + setParameter(parameters, base)
+ + " OR e.fullPath LIKE ?" + setParameter(
+ parameters, SyncopeConstants.ROOT_REALM.equals(base) ?
"/%" : base + "/%")).
+ collect(Collectors.joining(" OR "));
+
StringBuilder queryString = new StringBuilder("SELECT e FROM ").
append(JPARealm.class.getSimpleName()).append(" e ").
- append("WHERE (e.fullPath=?").
- append(setParameter(parameters, base)).
- append(" OR e.fullPath LIKE ?").
- append(setParameter(parameters,
SyncopeConstants.ROOT_REALM.equals(base) ? "/%" : base + "/%")).
- append(')');
+ append("WHERE (").append(basesClause).append(')');
if (keyword != null) {
queryString.append(" AND LOWER(e.name) LIKE ?").
@@ -117,9 +121,14 @@ public class JPARealmSearchDAO implements RealmSearchDAO {
@Override
public long countDescendants(final String base, final String keyword) {
+ return countDescendants(Set.of(base), keyword);
+ }
+
+ @Override
+ public long countDescendants(final Set<String> bases, final String
keyword) {
List<Object> parameters = new ArrayList<>();
- StringBuilder queryString = buildDescendantQuery(base, keyword,
parameters);
+ StringBuilder queryString = buildDescendantsQuery(bases, keyword,
parameters);
Query query = entityManager.createQuery(StringUtils.replaceOnce(
queryString.toString(),
"SELECT e ",
@@ -134,9 +143,14 @@ public class JPARealmSearchDAO implements RealmSearchDAO {
@Override
public List<Realm> findDescendants(final String base, final String
keyword, final Pageable pageable) {
+ return findDescendants(Set.of(base), keyword, pageable);
+ }
+
+ @Override
+ public List<Realm> findDescendants(final Set<String> bases, final String
keyword, final Pageable pageable) {
List<Object> parameters = new ArrayList<>();
- StringBuilder queryString = buildDescendantQuery(base, keyword,
parameters);
+ StringBuilder queryString = buildDescendantsQuery(bases, keyword,
parameters);
TypedQuery<Realm> query = entityManager.createQuery(
queryString.append(" ORDER BY e.fullPath").toString(),
Realm.class);
@@ -156,7 +170,7 @@ public class JPARealmSearchDAO implements RealmSearchDAO {
public List<String> findDescendants(final String base, final String
prefix) {
List<Object> parameters = new ArrayList<>();
- StringBuilder queryString = buildDescendantQuery(base, null,
parameters);
+ StringBuilder queryString = buildDescendantsQuery(Set.of(base), null,
parameters);
TypedQuery<Realm> query = entityManager.createQuery(queryString.
append(" AND (e.fullPath=?").
append(setParameter(parameters, prefix)).
diff --git
a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RealmTest.java
b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RealmTest.java
index 65dd66d28a..5dddbdb6cb 100644
---
a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RealmTest.java
+++
b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RealmTest.java
@@ -26,6 +26,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.util.List;
+import java.util.Set;
import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.types.EntityViolationType;
import
org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException;
@@ -103,6 +104,12 @@ public class RealmTest extends AbstractTest {
list.forEach(Assertions::assertNotNull);
assertEquals(4, realmDAO.findAll(Pageable.unpaged()).stream().count());
+
+ list = realmSearchDAO.findDescendants(Set.of("/even", "/odd"), null,
Pageable.unpaged());
+ assertEquals(3, list.size());
+ assertNotNull(list.stream().filter(realm ->
"even".equals(realm.getName())).findFirst().orElseThrow());
+ assertNotNull(list.stream().filter(realm ->
"two".equals(realm.getName())).findFirst().orElseThrow());
+ assertNotNull(list.stream().filter(realm ->
"odd".equals(realm.getName())).findFirst().orElseThrow());
}
@Test
diff --git
a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jRealmSearchDAO.java
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jRealmSearchDAO.java
index f7513d57f4..753879b62d 100644
---
a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jRealmSearchDAO.java
+++
b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jRealmSearchDAO.java
@@ -22,6 +22,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
import javax.cache.Cache;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.SyncopeConstants;
@@ -31,8 +34,6 @@ import
org.apache.syncope.core.persistence.api.dao.RealmSearchDAO;
import org.apache.syncope.core.persistence.api.entity.Realm;
import org.apache.syncope.core.persistence.neo4j.entity.EntityCacheKey;
import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRealm;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Pageable;
import org.springframework.data.neo4j.core.Neo4jClient;
import org.springframework.data.neo4j.core.Neo4jTemplate;
@@ -40,17 +41,21 @@ import
org.springframework.transaction.annotation.Transactional;
public class Neo4jRealmSearchDAO extends AbstractDAO implements RealmSearchDAO
{
- protected static final Logger LOG =
LoggerFactory.getLogger(RealmSearchDAO.class);
-
- protected static StringBuilder buildDescendantQuery(
- final String base,
+ protected static StringBuilder buildDescendantsQuery(
+ final Set<String> bases,
final String keyword,
final Map<String, Object> parameters) {
+ AtomicInteger index = new AtomicInteger(0);
+ String basesClause = bases.stream().map(base -> {
+ int idx = index.incrementAndGet();
+ parameters.put("base" + idx, base);
+ parameters.put("like" + idx,
SyncopeConstants.ROOT_REALM.equals(base) ? "/.*" : base + "/.*");
+ return "n.fullPath = $base" + idx + " OR n.fullPath =~ $like" +
idx;
+ }).collect(Collectors.joining(" OR "));
+
StringBuilder queryString = new StringBuilder("MATCH
(n:").append(Neo4jRealm.NODE).append(") ").
- append("WHERE (n.fullPath = $base OR n.fullPath =~ $like)");
- parameters.put("base", base);
- parameters.put("like", SyncopeConstants.ROOT_REALM.equals(base) ?
"/.*" : base + "/.*");
+ append("WHERE (").append(basesClause).append(')');
if (keyword != null) {
queryString.append(" AND toLower(n.name) =~ $name");
@@ -103,17 +108,27 @@ public class Neo4jRealmSearchDAO extends AbstractDAO
implements RealmSearchDAO {
@Override
public long countDescendants(final String base, final String keyword) {
+ return countDescendants(Set.of(base), keyword);
+ }
+
+ @Override
+ public long countDescendants(final Set<String> bases, final String
keyword) {
Map<String, Object> parameters = new HashMap<>();
- StringBuilder queryString = buildDescendantQuery(base, keyword,
parameters).append(" RETURN COUNT(n)");
+ StringBuilder queryString = buildDescendantsQuery(bases, keyword,
parameters).append(" RETURN COUNT(n)");
return neo4jTemplate.count(queryString.toString(), parameters);
}
@Override
public List<Realm> findDescendants(final String base, final String
keyword, final Pageable pageable) {
+ return findDescendants(Set.of(base), keyword, pageable);
+ }
+
+ @Override
+ public List<Realm> findDescendants(final Set<String> bases, final String
keyword, final Pageable pageable) {
Map<String, Object> parameters = new HashMap<>();
- StringBuilder queryString = buildDescendantQuery(base, keyword,
parameters).
+ StringBuilder queryString = buildDescendantsQuery(bases, keyword,
parameters).
append(" RETURN n.id ORDER BY n.fullPath");
if (pageable.isPaged()) {
queryString.append(" SKIP ").append(pageable.getPageSize() *
pageable.getPageNumber()).
@@ -128,7 +143,7 @@ public class Neo4jRealmSearchDAO extends AbstractDAO
implements RealmSearchDAO {
public List<String> findDescendants(final String base, final String
prefix) {
Map<String, Object> parameters = new HashMap<>();
- StringBuilder queryString = buildDescendantQuery(base, null,
parameters).
+ StringBuilder queryString = buildDescendantsQuery(Set.of(base), null,
parameters).
append(" AND (n.fullPath = $prefix OR n.fullPath =~
$likePrefix)").
append(" RETURN n.id ORDER BY n.fullPath");
parameters.put("prefix", prefix);
diff --git
a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RealmTest.java
b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RealmTest.java
index 09b5580753..578cb3f891 100644
---
a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RealmTest.java
+++
b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/RealmTest.java
@@ -26,6 +26,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.util.List;
+import java.util.Set;
import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.types.EntityViolationType;
import
org.apache.syncope.core.persistence.api.attrvalue.InvalidEntityException;
@@ -115,6 +116,12 @@ public class RealmTest extends AbstractTest {
list.forEach(Assertions::assertNotNull);
assertEquals(4,
realmDAO.findAll(Pageable.ofSize(100)).stream().count());
+
+ list = realmSearchDAO.findDescendants(Set.of("/even", "/odd"), null,
Pageable.unpaged());
+ assertEquals(3, list.size());
+ assertNotNull(list.stream().filter(realm ->
"even".equals(realm.getName())).findFirst().orElseThrow());
+ assertNotNull(list.stream().filter(realm ->
"two".equals(realm.getName())).findFirst().orElseThrow());
+ assertNotNull(list.stream().filter(realm ->
"odd".equals(realm.getName())).findFirst().orElseThrow());
}
@Test
diff --git
a/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchRealmSearchDAO.java
b/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchRealmSearchDAO.java
index 2208ad5c6a..265409d09c 100644
---
a/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchRealmSearchDAO.java
+++
b/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchRealmSearchDAO.java
@@ -29,8 +29,10 @@ import
co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders;
import co.elastic.clients.elasticsearch.core.CountRequest;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.search.Hit;
+import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.core.persistence.api.dao.MalformedPathException;
@@ -139,13 +141,16 @@ public class ElasticsearchRealmSearchDAO implements
RealmSearchDAO {
flatMap(Optional::stream).map(Realm.class::cast).toList();
}
- protected Query buildDescendantQuery(final String base, final String
keyword) {
- Query prefix = new
Query.Builder().disMax(QueryBuilders.disMax().queries(
- new Query.Builder().term(QueryBuilders.term().
- field("fullPath").value(base).build()).build(),
- new Query.Builder().regexp(QueryBuilders.regexp().
-
field("fullPath").value(SyncopeConstants.ROOT_REALM.equals(base) ? "/.*" : base
+ "/.*").
- build()).build()).build()).build();
+ protected Query buildDescendantsQuery(final Set<String> bases, final
String keyword) {
+ List<Query> basesQueries = new ArrayList<>();
+ bases.forEach(base -> {
+ basesQueries.add(new Query.Builder().term(QueryBuilders.term().
+ field("fullPath").value(base).build()).build());
+ basesQueries.add(new Query.Builder().regexp(QueryBuilders.regexp().
+
field("fullPath").value(SyncopeConstants.ROOT_REALM.equals(base) ? "/.*" : base
+ "/.*").
+ build()).build());
+ });
+ Query prefix = new
Query.Builder().disMax(QueryBuilders.disMax().queries(basesQueries).build()).build();
if (keyword == null) {
return prefix;
@@ -175,9 +180,14 @@ public class ElasticsearchRealmSearchDAO implements
RealmSearchDAO {
@Override
public long countDescendants(final String base, final String keyword) {
+ return countDescendants(Set.of(base), keyword);
+ }
+
+ @Override
+ public long countDescendants(final Set<String> bases, final String
keyword) {
CountRequest request = new CountRequest.Builder().
index(ElasticsearchUtils.getRealmIndex(AuthContextUtils.getDomain())).
- query(buildDescendantQuery(base, keyword)).
+ query(buildDescendantsQuery(bases, keyword)).
build();
try {
@@ -190,10 +200,15 @@ public class ElasticsearchRealmSearchDAO implements
RealmSearchDAO {
@Override
public List<Realm> findDescendants(final String base, final String
keyword, final Pageable pageable) {
+ return findDescendants(Set.of(base), keyword, pageable);
+ }
+
+ @Override
+ public List<Realm> findDescendants(final Set<String> bases, final String
keyword, final Pageable pageable) {
SearchRequest request = new SearchRequest.Builder().
index(ElasticsearchUtils.getRealmIndex(AuthContextUtils.getDomain())).
searchType(SearchType.QueryThenFetch).
- query(buildDescendantQuery(base, keyword)).
+ query(buildDescendantsQuery(bases, keyword)).
from(pageable.isUnpaged() ? 0 : pageable.getPageSize() *
pageable.getPageNumber()).
size(pageable.isUnpaged() ? indexMaxResultWindow :
pageable.getPageSize()).
sort(REALM_SORT_OPTIONS).
@@ -222,8 +237,7 @@ public class ElasticsearchRealmSearchDAO implements
RealmSearchDAO {
build()).build()).build()).build();
Query query = new Query.Builder().bool(QueryBuilders.bool().must(
- buildDescendantQuery(base, (String) null),
- prefixQuery).build()).
+ buildDescendantsQuery(Set.of(base), (String) null),
prefixQuery).build()).
build();
SearchRequest request = new SearchRequest.Builder().
diff --git
a/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchRealmSearchDAO.java
b/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchRealmSearchDAO.java
index d882119faa..21e88c9eed 100644
---
a/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchRealmSearchDAO.java
+++
b/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchRealmSearchDAO.java
@@ -18,8 +18,10 @@
*/
package org.apache.syncope.core.persistence.opensearch.dao;
+import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.core.persistence.api.dao.MalformedPathException;
@@ -139,14 +141,16 @@ public class OpenSearchRealmSearchDAO implements
RealmSearchDAO {
flatMap(Optional::stream).map(Realm.class::cast).toList();
}
- protected Query buildDescendantQuery(final String base, final String
keyword) {
- Query prefix = new
Query.Builder().disMax(QueryBuilders.disMax().queries(
- new Query.Builder().term(QueryBuilders.term().
-
field("fullPath").value(FieldValue.of(base)).build()).build(),
- new Query.Builder().regexp(QueryBuilders.regexp().
-
field("fullPath").value(SyncopeConstants.ROOT_REALM.equals(base) ? "/.*" : base
+ "/.*").
- build()).build()).build()).build();
-
+ protected Query buildDescendantsQuery(final Set<String> bases, final
String keyword) {
+ List<Query> basesQueries = new ArrayList<>();
+ bases.forEach(base -> {
+ basesQueries.add(new Query.Builder().term(QueryBuilders.term().
+
field("fullPath").value(FieldValue.of(base)).build()).build());
+ basesQueries.add(new Query.Builder().regexp(QueryBuilders.regexp().
+
field("fullPath").value(SyncopeConstants.ROOT_REALM.equals(base) ? "/.*" : base
+ "/.*").
+ build()).build());
+ });
+ Query prefix = new
Query.Builder().disMax(QueryBuilders.disMax().queries(basesQueries).build()).build();
if (keyword == null) {
return prefix;
}
@@ -175,9 +179,14 @@ public class OpenSearchRealmSearchDAO implements
RealmSearchDAO {
@Override
public long countDescendants(final String base, final String keyword) {
+ return countDescendants(Set.of(base), keyword);
+ }
+
+ @Override
+ public long countDescendants(final Set<String> bases, final String
keyword) {
CountRequest request = new CountRequest.Builder().
index(OpenSearchUtils.getRealmIndex(AuthContextUtils.getDomain())).
- query(buildDescendantQuery(base, keyword)).
+ query(buildDescendantsQuery(bases, keyword)).
build();
try {
@@ -190,10 +199,15 @@ public class OpenSearchRealmSearchDAO implements
RealmSearchDAO {
@Override
public List<Realm> findDescendants(final String base, final String
keyword, final Pageable pageable) {
+ return findDescendants(Set.of(base), keyword, pageable);
+ }
+
+ @Override
+ public List<Realm> findDescendants(final Set<String> bases, final String
keyword, final Pageable pageable) {
SearchRequest request = new SearchRequest.Builder().
index(OpenSearchUtils.getRealmIndex(AuthContextUtils.getDomain())).
searchType(SearchType.QueryThenFetch).
- query(buildDescendantQuery(base, keyword)).
+ query(buildDescendantsQuery(bases, keyword)).
from(pageable.isUnpaged() ? 0 : pageable.getPageSize() *
pageable.getPageNumber()).
size(pageable.isUnpaged() ? indexMaxResultWindow :
pageable.getPageSize()).
sort(REALM_SORT_OPTIONS).
@@ -222,8 +236,7 @@ public class OpenSearchRealmSearchDAO implements
RealmSearchDAO {
build()).build()).build()).build();
Query query = new Query.Builder().bool(QueryBuilders.bool().must(
- buildDescendantQuery(base, null),
- prefixQuery).build()).
+ buildDescendantsQuery(Set.of(base), (String) null),
prefixQuery).build()).
build();
SearchRequest request = new SearchRequest.Builder().
diff --git
a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestCommand.java
b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestCommand.java
index 462570ab80..ae5d83859e 100644
---
a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestCommand.java
+++
b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestCommand.java
@@ -19,6 +19,7 @@
package org.apache.syncope.fit.core.reference;
import java.util.Optional;
+import java.util.Set;
import org.apache.syncope.common.lib.Attr;
import org.apache.syncope.common.lib.request.AnyObjectCR;
import org.apache.syncope.common.lib.to.AnyObjectTO;
@@ -44,7 +45,7 @@ public class TestCommand implements Command<TestCommandArgs> {
private AnyObjectLogic anyObjectLogic;
private Optional<RealmTO> getRealm(final String fullPath) {
- return realmLogic.search(null, fullPath, Pageable.unpaged()).get().
+ return realmLogic.search(null, Set.of(fullPath),
Pageable.unpaged()).get().
filter(realm ->
fullPath.equals(realm.getFullPath())).findFirst();
}
diff --git
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java
index 219e82b5df..1c29a6b472 100644
---
a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java
+++
b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/RealmITCase.java
@@ -18,6 +18,7 @@
*/
package org.apache.syncope.fit.core;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -465,4 +466,15 @@ public class RealmITCase extends AbstractITCase {
ROLE_SERVICE.delete(role.getKey());
}
}
+
+ @Test
+ public void issueSYNCOPE1871() {
+ PagedResult<RealmTO> result = REALM_SERVICE.search(new
RealmQuery.Builder().base("/odd").base("/even").build());
+ assertDoesNotThrow(() -> result.getResult().stream().
+ filter(r ->
"odd".equals(r.getName())).findFirst().orElseThrow());
+ assertDoesNotThrow(() -> result.getResult().stream().
+ filter(r ->
"even".equals(r.getName())).findFirst().orElseThrow());
+ assertDoesNotThrow(() -> result.getResult().stream().
+ filter(r ->
"two".equals(r.getName())).findFirst().orElseThrow());
+ }
}