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 2019/10/09 13:49:56 UTC
[syncope] 01/02: [SYNCOPE-1501] Connector Objects can now be
filtered via FIQL
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
commit e0c2fd972523fe2d34f5ce1e98db23c2f63989f5
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Wed Oct 9 14:33:54 2019 +0200
[SYNCOPE-1501] Connector Objects can now be filtered via FIQL
---
.../client/console/rest/ResourceRestClient.java | 6 +-
.../apache/syncope/client/lib/SyncopeClient.java | 10 +
.../common/rest/api/service/ResourceService.java | 15 +-
.../search/AbstractFiqlSearchConditionBuilder.java | 1 -
.../AnyObjectFiqlSearchConditionBuilder.java | 1 -
.../ConnObjectTOFiqlSearchConditionBuilder.java | 80 ++++++
.../search/GroupFiqlSearchConditionBuilder.java | 1 -
...jectTOListQuery.java => ConnObjectTOQuery.java} | 33 ++-
.../apache/syncope/core/logic/ResourceLogic.java | 133 +++++-----
.../core/rest/cxf/service/ResourceServiceImpl.java | 49 +++-
.../persistence/api/search/FilterConverter.java | 64 +++++
.../core/persistence/api/search/FilterVisitor.java | 166 ++++++++++++
.../persistence/api/search/SearchCondVisitor.java | 26 +-
.../api/search/FilterConverterTest.java | 289 +++++++++++++++++++++
.../syncope/fit/core/LinkedAccountITCase.java | 34 +--
.../apache/syncope/fit/core/ResourceITCase.java | 58 -----
.../org/apache/syncope/fit/core/SearchITCase.java | 122 ++++++++-
17 files changed, 907 insertions(+), 181 deletions(-)
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/rest/ResourceRestClient.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/rest/ResourceRestClient.java
index 71dfd5c..ff5e201 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/rest/ResourceRestClient.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/rest/ResourceRestClient.java
@@ -26,7 +26,7 @@ import org.apache.commons.lang3.tuple.Pair;
import org.apache.syncope.common.lib.to.ConnObjectTO;
import org.apache.syncope.common.lib.to.PagedConnObjectTOResult;
import org.apache.syncope.common.lib.to.ResourceTO;
-import org.apache.syncope.common.rest.api.beans.ConnObjectTOListQuery;
+import org.apache.syncope.common.rest.api.beans.ConnObjectTOQuery;
import org.apache.syncope.common.rest.api.service.ResourceService;
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
@@ -62,7 +62,7 @@ public class ResourceRestClient extends BaseRestClient {
final String pagedResultCookie,
final SortParam<String> sort) {
- ConnObjectTOListQuery.Builder builder = new ConnObjectTOListQuery.Builder().
+ ConnObjectTOQuery.Builder builder = new ConnObjectTOQuery.Builder().
pagedResultsCookie(pagedResultCookie).
size(size).
orderBy(toOrderBy(sort));
@@ -72,7 +72,7 @@ public class ResourceRestClient extends BaseRestClient {
PagedConnObjectTOResult list;
try {
- list = getService(ResourceService.class).listConnObjects(resource, anyTypeKey, builder.build());
+ list = getService(ResourceService.class).searchConnObjects(resource, anyTypeKey, builder.build());
result.addAll(list.getResult());
nextPageResultCookie = list.getPagedResultsCookie();
} catch (Exception e) {
diff --git a/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java b/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java
index d5e6362..2979c9b 100644
--- a/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java
+++ b/client/idrepo/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClient.java
@@ -42,6 +42,7 @@ import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transport.http.URLConnectionHTTPConduit;
import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.search.AnyObjectFiqlSearchConditionBuilder;
+import org.apache.syncope.common.lib.search.ConnObjectTOFiqlSearchConditionBuilder;
import org.apache.syncope.common.lib.search.OrderByClauseBuilder;
import org.apache.syncope.common.lib.search.GroupFiqlSearchConditionBuilder;
import org.apache.syncope.common.lib.search.UserFiqlSearchConditionBuilder;
@@ -188,6 +189,15 @@ public class SyncopeClient {
}
/**
+ * Returns a new instance of {@link ConnObjectTOFiqlSearchConditionBuilder}, for assisted building of FIQL queries.
+ *
+ * @return default instance of {@link ConnObjectTOFiqlSearchConditionBuilder}
+ */
+ public static ConnObjectTOFiqlSearchConditionBuilder getConnObjectTOFiqlSearchConditionBuilder() {
+ return new ConnObjectTOFiqlSearchConditionBuilder();
+ }
+
+ /**
* Returns a new instance of {@link OrderByClauseBuilder}, for assisted building of {@code orderby} clauses.
*
* @return default instance of {@link OrderByClauseBuilder}
diff --git a/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceService.java b/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceService.java
index 27b7813..cca2ac0 100644
--- a/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceService.java
+++ b/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ResourceService.java
@@ -45,7 +45,7 @@ import org.apache.syncope.common.lib.to.ConnObjectTO;
import org.apache.syncope.common.lib.to.PagedConnObjectTOResult;
import org.apache.syncope.common.lib.to.ResourceTO;
import org.apache.syncope.common.rest.api.RESTHeaders;
-import org.apache.syncope.common.rest.api.beans.ConnObjectTOListQuery;
+import org.apache.syncope.common.rest.api.beans.ConnObjectTOQuery;
/**
* REST operations for external resources.
@@ -62,16 +62,17 @@ public interface ResourceService extends JAXRSService {
*
* @param key name of resource to read connector object from
* @param anyTypeKey any object type
- * @param anyKey any object key
+ * @param value if value looks like a UUID then it is interpreted as user, group or any object key, otherwise
+ * as key value on the resource
* @return connector object from the external resource, for the given type and key
*/
@GET
- @Path("{key}/{anyTypeKey}/{anyKey}")
+ @Path("{key}/{anyTypeKey}/{value}")
@Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
ConnObjectTO readConnObject(
@NotNull @PathParam("key") String key,
@NotNull @PathParam("anyTypeKey") String anyTypeKey,
- @NotNull @PathParam("anyKey") String anyKey);
+ @NotNull @PathParam("value") String value);
/**
* Returns a paged list of connector objects from external resource, for the given type, matching
@@ -79,16 +80,16 @@ public interface ResourceService extends JAXRSService {
*
* @param key name of resource to read connector object from
* @param anyTypeKey any object type
- * @param listQuery query conditions
+ * @param connObjectTOQuery query conditions
* @return connector objects from the external resource, for the given type
*/
@GET
@Path("{key}/{anyTypeKey}")
@Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
- PagedConnObjectTOResult listConnObjects(
+ PagedConnObjectTOResult searchConnObjects(
@NotNull @PathParam("key") String key,
@NotNull @PathParam("anyTypeKey") String anyTypeKey,
- @BeanParam ConnObjectTOListQuery listQuery);
+ @BeanParam ConnObjectTOQuery connObjectTOQuery);
/**
* Returns the resource with matching name.
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/AbstractFiqlSearchConditionBuilder.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/AbstractFiqlSearchConditionBuilder.java
index 18fee59..5f8aa43 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/AbstractFiqlSearchConditionBuilder.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/AbstractFiqlSearchConditionBuilder.java
@@ -146,6 +146,5 @@ public abstract class AbstractFiqlSearchConditionBuilder extends FiqlSearchCondi
this.result = SpecialAttr.DYNREALMS.toString();
return condition(FiqlParser.NEQ, dynRealm, (Object[]) moreDynRealms);
}
-
}
}
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/AnyObjectFiqlSearchConditionBuilder.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/AnyObjectFiqlSearchConditionBuilder.java
index 9276015..b497c7b 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/AnyObjectFiqlSearchConditionBuilder.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/AnyObjectFiqlSearchConditionBuilder.java
@@ -162,6 +162,5 @@ public class AnyObjectFiqlSearchConditionBuilder extends AbstractFiqlSearchCondi
this.result = SpecialAttr.ASSIGNABLE.toString();
return condition(FiqlParser.EQ, SpecialAttr.NULL);
}
-
}
}
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/ConnObjectTOFiqlSearchConditionBuilder.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/ConnObjectTOFiqlSearchConditionBuilder.java
new file mode 100644
index 0000000..d028a71
--- /dev/null
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/ConnObjectTOFiqlSearchConditionBuilder.java
@@ -0,0 +1,80 @@
+/*
+ * 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.common.lib.search;
+
+import java.util.Map;
+import org.apache.cxf.jaxrs.ext.search.client.CompleteCondition;
+
+/**
+ * Extends {@link AbstractFiqlSearchConditionBuilder} by providing some additional facilities for searching
+ * connector objects.
+ */
+public class ConnObjectTOFiqlSearchConditionBuilder extends AbstractFiqlSearchConditionBuilder {
+
+ private static final long serialVersionUID = 4983742159694010935L;
+
+ @Override
+ protected Builder newBuilderInstance() {
+ return new Builder(properties);
+ }
+
+ @Override
+ public SyncopeProperty is(final String property) {
+ return newBuilderInstance().is(property);
+ }
+
+ protected class Builder extends AbstractFiqlSearchConditionBuilder.Builder
+ implements SyncopeProperty, CompleteCondition {
+
+ public Builder(final Map<String, String> properties) {
+ super(properties);
+ }
+
+ public Builder(final Builder parent) {
+ super(parent);
+ }
+
+ @Override
+ public SyncopeProperty is(final String property) {
+ Builder b = new Builder(this);
+ b.result = property;
+ return b;
+ }
+
+ @Override
+ public CompleteCondition inDynRealms(final String dynRealm, final String... moreDynRealms) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public CompleteCondition notInDynRealms(final String dynRealm, final String... moreDynRealms) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public CompleteCondition hasResources(final String resource, final String... moreResources) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public CompleteCondition hasNotResources(final String resource, final String... moreResources) {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/GroupFiqlSearchConditionBuilder.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/GroupFiqlSearchConditionBuilder.java
index 232ba6b..1a875c9 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/GroupFiqlSearchConditionBuilder.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/search/GroupFiqlSearchConditionBuilder.java
@@ -93,6 +93,5 @@ public class GroupFiqlSearchConditionBuilder extends AbstractFiqlSearchCondition
this.result = SpecialAttr.MEMBER.toString();
return condition(FiqlParser.NEQ, member, (Object[]) moreMembers);
}
-
}
}
diff --git a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ConnObjectTOListQuery.java b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ConnObjectTOQuery.java
similarity index 79%
rename from common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ConnObjectTOListQuery.java
rename to common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ConnObjectTOQuery.java
index 9c8d172..db874ce 100644
--- a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ConnObjectTOListQuery.java
+++ b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ConnObjectTOQuery.java
@@ -19,15 +19,13 @@
package org.apache.syncope.common.rest.api.beans;
import java.io.Serializable;
-import java.util.Optional;
-
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.QueryParam;
import org.apache.syncope.common.rest.api.service.JAXRSService;
-public class ConnObjectTOListQuery implements Serializable {
+public class ConnObjectTOQuery implements Serializable {
private static final long serialVersionUID = -371488230250055359L;
@@ -35,7 +33,7 @@ public class ConnObjectTOListQuery implements Serializable {
public static class Builder {
- private final ConnObjectTOListQuery instance = new ConnObjectTOListQuery();
+ private final ConnObjectTOQuery instance = new ConnObjectTOQuery();
public Builder size(final Integer size) {
instance.setSize(size);
@@ -52,10 +50,14 @@ public class ConnObjectTOListQuery implements Serializable {
return this;
}
- public ConnObjectTOListQuery build() {
- return instance;
+ public Builder fiql(final String fiql) {
+ instance.setFiql(fiql);
+ return this;
}
+ public ConnObjectTOQuery build() {
+ return instance;
+ }
}
private Integer size;
@@ -64,10 +66,14 @@ public class ConnObjectTOListQuery implements Serializable {
private String orderBy;
+ private String fiql;
+
public Integer getSize() {
- return Optional.ofNullable(size).map(integer -> integer > MAX_SIZE
- ? MAX_SIZE
- : integer).orElse(25);
+ return size == null
+ ? 25
+ : size > MAX_SIZE
+ ? MAX_SIZE
+ : size;
}
@Min(1)
@@ -95,4 +101,13 @@ public class ConnObjectTOListQuery implements Serializable {
public void setOrderBy(final String orderBy) {
this.orderBy = orderBy;
}
+
+ public String getFiql() {
+ return fiql;
+ }
+
+ @QueryParam(JAXRSService.PARAM_FIQL)
+ public void setFiql(final String fiql) {
+ this.fiql = fiql;
+ }
}
diff --git a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
index 704cbe8..9dd0c93 100644
--- a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
+++ b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
@@ -27,37 +27,32 @@ import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.lang3.tuple.ImmutableTriple;
import org.apache.commons.lang3.tuple.Pair;
-import org.apache.commons.lang3.tuple.Triple;
import org.apache.syncope.common.lib.collections.IteratorChain;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.to.ConnObjectTO;
import org.apache.syncope.common.lib.to.ResourceTO;
-import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.common.lib.types.ClientExceptionType;
import org.apache.syncope.common.lib.types.IdMEntitlement;
import org.apache.syncope.core.persistence.api.dao.DuplicateException;
import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
import org.apache.syncope.core.persistence.api.dao.NotFoundException;
-import org.apache.syncope.core.persistence.api.dao.GroupDAO;
-import org.apache.syncope.core.persistence.api.dao.UserDAO;
-import org.apache.syncope.core.persistence.api.entity.VirSchema;
import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
import org.apache.syncope.core.provisioning.api.Connector;
import org.apache.syncope.core.provisioning.api.ConnectorFactory;
import org.apache.syncope.core.provisioning.api.data.ResourceDataBinder;
import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
-import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO;
import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO;
import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
import org.apache.syncope.core.persistence.api.entity.Any;
import org.apache.syncope.core.persistence.api.entity.AnyType;
+import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
import org.apache.syncope.core.persistence.api.entity.ConnInstance;
+import org.apache.syncope.core.persistence.api.entity.VirSchema;
import org.apache.syncope.core.persistence.api.entity.resource.Provision;
import org.apache.syncope.core.provisioning.api.MappingManager;
import org.apache.syncope.core.provisioning.api.data.ConnInstanceDataBinder;
@@ -74,6 +69,7 @@ import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.OperationOptions;
import org.identityconnectors.framework.common.objects.SearchResult;
import org.identityconnectors.framework.common.objects.Uid;
+import org.identityconnectors.framework.common.objects.filter.Filter;
import org.identityconnectors.framework.spi.SearchResultsHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -90,18 +86,9 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
private AnyTypeDAO anyTypeDAO;
@Autowired
- private AnyObjectDAO anyObjectDAO;
-
- @Autowired
private ConnInstanceDAO connInstanceDAO;
@Autowired
- private UserDAO userDAO;
-
- @Autowired
- private GroupDAO groupDAO;
-
- @Autowired
private VirSchemaDAO virSchemaDAO;
@Autowired
@@ -116,6 +103,9 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
@Autowired
private ConnectorFactory connFactory;
+ @Autowired
+ private AnyUtilsFactory anyUtilsFactory;
+
protected static void securityChecks(final Set<String> effectiveRealms, final String realm, final String key) {
boolean authorized = effectiveRealms.stream().anyMatch(realm::startsWith);
if (!authorized) {
@@ -277,69 +267,57 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
return resourceDAO.findAll().stream().map(binder::getResourceTO).collect(Collectors.toList());
}
- private Triple<ExternalResource, AnyType, Provision> connObjectInit(
+ private Pair<AnyType, Provision> connObjectInit(
final String resourceKey, final String anyTypeKey) {
ExternalResource resource = resourceDAO.authFind(resourceKey);
if (resource == null) {
throw new NotFoundException("Resource '" + resourceKey + '\'');
}
+
AnyType anyType = anyTypeDAO.find(anyTypeKey);
if (anyType == null) {
throw new NotFoundException("AnyType '" + anyTypeKey + '\'');
}
- Optional<? extends Provision> provision = resource.getProvision(anyType);
- if (provision.isEmpty()) {
- throw new NotFoundException("Provision on resource '" + resourceKey + "' for type '" + anyTypeKey + '\'');
- }
- return ImmutableTriple.of(resource, anyType, provision.get());
- }
+ Provision provision = resource.getProvision(anyType).
+ orElseThrow(() -> new NotFoundException(
+ "Provision on resource '" + resourceKey + "' for type '" + anyTypeKey + "'"));
- @PreAuthorize("hasRole('" + IdMEntitlement.RESOURCE_GET_CONNOBJECT + "')")
- @Transactional(readOnly = true)
- public ConnObjectTO readConnObject(final String key, final String anyTypeKey, final String anyKey) {
- Triple<ExternalResource, AnyType, Provision> init = connObjectInit(key, anyTypeKey);
+ return Pair.of(anyType, provision);
+ }
- // 1. find any
- Any<?> any = init.getMiddle().getKind() == AnyTypeKind.USER
- ? userDAO.find(anyKey)
- : init.getMiddle().getKind() == AnyTypeKind.ANY_OBJECT
- ? anyObjectDAO.find(anyKey)
- : groupDAO.find(anyKey);
- if (any == null) {
- throw new NotFoundException(init.getMiddle() + " " + anyKey);
- }
+ private ConnObjectTO readConnObject(
+ final Provision provision,
+ final String connObjectKeyValue) {
- // 2. build connObjectKeyItem
- MappingItem connObjectKeyItem = MappingUtils.getConnObjectKeyItem(init.getRight()).
+ // 0. build connObjectKeyItem
+ MappingItem connObjectKeyItem = MappingUtils.getConnObjectKeyItem(provision).
orElseThrow(() -> new NotFoundException(
- "ConnObjectKey mapping for " + init.getMiddle() + ' ' + anyKey + " on resource '" + key + '\''));
- String connObjectKeyValue = mappingManager.getConnObjectKeyValue(any, init.getRight()).
- orElseThrow(() -> new NotFoundException(
- "ConnObjectKey value for " + init.getMiddle() + ' ' + anyKey + " on resource '" + key + '\''));
+ "ConnObjectKey mapping for " + provision.getAnyType().getKey()
+ + " on resource '" + provision.getResource().getKey() + "'"));
- // 3. determine attributes to query
- Set<MappingItem> linkinMappingItems = virSchemaDAO.findByProvision(init.getRight()).stream().
- map(VirSchema::asLinkingMappingItem).collect(Collectors.toSet());
+ // 1. determine attributes to query
+ Set<MappingItem> linkinMappingItems = virSchemaDAO.findByProvision(provision).stream().
+ map(virSchema -> virSchema.asLinkingMappingItem()).collect(Collectors.toSet());
Iterator<MappingItem> mapItems = new IteratorChain<>(
- init.getRight().getMapping().getItems().iterator(),
+ provision.getMapping().getItems().iterator(),
linkinMappingItems.iterator());
- // 4. read from the underlying connector
- Connector connector = connFactory.getConnector(init.getLeft());
+ // 2. read from the underlying connector
+ Connector connector = connFactory.getConnector(provision.getResource());
ConnectorObject connectorObject = connector.getObject(
- init.getRight().getObjectClass(),
+ provision.getObjectClass(),
AttributeBuilder.build(connObjectKeyItem.getExtAttrName(), connObjectKeyValue),
- init.getRight().isIgnoreCaseMatch(),
+ provision.isIgnoreCaseMatch(),
MappingUtils.buildOperationOptions(mapItems));
if (connectorObject == null) {
throw new NotFoundException(
- "Object " + connObjectKeyValue + " with class " + init.getRight().getObjectClass()
- + " not found on resource " + key);
+ "Object " + connObjectKeyValue + " with class " + provision.getObjectClass()
+ + " not found on resource " + provision.getResource().getKey());
}
- // 5. build result
+ // 3. build result
Set<Attribute> attributes = connectorObject.getAttributes();
if (AttributeUtil.find(Uid.NAME, attributes) == null) {
attributes.add(connectorObject.getUid());
@@ -351,10 +329,49 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
return ConnObjectUtils.getConnObjectTO(connectorObject);
}
+ @PreAuthorize("hasRole('" + IdMEntitlement.RESOURCE_GET_CONNOBJECT + "')")
+ @Transactional(readOnly = true)
+ public ConnObjectTO readConnObjectByAnyKey(
+ final String key,
+ final String anyTypeKey,
+ final String anyKey) {
+
+ Pair<AnyType, Provision> init = connObjectInit(key, anyTypeKey);
+
+ // 1. find any
+ Any<?> any = anyUtilsFactory.getInstance(init.getLeft().getKind()).dao().authFind(anyKey);
+ if (any == null) {
+ throw new NotFoundException(init.getLeft() + " " + anyKey);
+ }
+
+ // 2. find connObjectKeyValue
+ String connObjectKeyValue = mappingManager.getConnObjectKeyValue(any, init.getRight()).
+ orElseThrow(() -> new NotFoundException(
+ "ConnObjectKey value for " + init.getLeft() + " " + anyKey + " on resource '" + key + "'"));
+
+ return readConnObject(init.getRight(), connObjectKeyValue);
+ }
+
+ @PreAuthorize("hasRole('" + IdMEntitlement.RESOURCE_GET_CONNOBJECT + "')")
+ @Transactional(readOnly = true)
+ public ConnObjectTO readConnObjectByConnObjectKey(
+ final String key,
+ final String anyTypeKey,
+ final String connObjectKeyValue) {
+
+ Pair<AnyType, Provision> init = connObjectInit(key, anyTypeKey);
+ return readConnObject(init.getRight(), connObjectKeyValue);
+ }
+
@PreAuthorize("hasRole('" + IdMEntitlement.RESOURCE_LIST_CONNOBJECT + "')")
@Transactional(readOnly = true)
- public Pair<SearchResult, List<ConnObjectTO>> listConnObjects(final String key, final String anyTypeKey,
- final int size, final String pagedResultsCookie, final List<OrderByClause> orderBy) {
+ public Pair<SearchResult, List<ConnObjectTO>> searchConnObjects(
+ final Filter filter,
+ final String key,
+ final String anyTypeKey,
+ final int size,
+ final String pagedResultsCookie,
+ final List<OrderByClause> orderBy) {
ExternalResource resource;
ObjectClass objectClass;
@@ -372,8 +389,8 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
options = MappingUtils.buildOperationOptions(
MappingUtils.getPropagationItems(resource.getOrgUnit().getItems()).iterator());
} else {
- Triple<ExternalResource, AnyType, Provision> init = connObjectInit(key, anyTypeKey);
- resource = init.getLeft();
+ Pair<AnyType, Provision> init = connObjectInit(key, anyTypeKey);
+ resource = init.getRight().getResource();
objectClass = init.getRight().getObjectClass();
init.getRight().getMapping().getItems();
@@ -387,7 +404,7 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
List<ConnObjectTO> connObjects = new ArrayList<>();
SearchResult searchResult = connFactory.getConnector(resource).
- search(objectClass, null, new SearchResultsHandler() {
+ search(objectClass, filter, new SearchResultsHandler() {
private int count;
diff --git a/core/idm/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ResourceServiceImpl.java b/core/idm/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ResourceServiceImpl.java
index 2076fbb..ea255de 100644
--- a/core/idm/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ResourceServiceImpl.java
+++ b/core/idm/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ResourceServiceImpl.java
@@ -25,15 +25,23 @@ import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.tuple.Pair;
+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.SyncopeConstants;
import org.apache.syncope.common.lib.to.ConnObjectTO;
import org.apache.syncope.common.lib.to.PagedConnObjectTOResult;
import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
import org.apache.syncope.common.rest.api.RESTHeaders;
-import org.apache.syncope.common.rest.api.beans.ConnObjectTOListQuery;
+import org.apache.syncope.common.rest.api.beans.ConnObjectTOQuery;
import org.apache.syncope.common.rest.api.service.ResourceService;
import org.apache.syncope.core.logic.ResourceLogic;
+import org.apache.syncope.core.persistence.api.search.FilterVisitor;
import org.identityconnectors.framework.common.objects.SearchResult;
+import org.identityconnectors.framework.common.objects.filter.Filter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -83,16 +91,41 @@ public class ResourceServiceImpl extends AbstractServiceImpl implements Resource
}
@Override
- public ConnObjectTO readConnObject(final String key, final String anyTypeKey, final String anyKey) {
- return logic.readConnObject(key, anyTypeKey, anyKey);
+ public ConnObjectTO readConnObject(final String key, final String anyTypeKey, final String value) {
+ return SyncopeConstants.UUID_PATTERN.matcher(value).matches()
+ ? logic.readConnObjectByAnyKey(key, anyTypeKey, value)
+ : logic.readConnObjectByConnObjectKey(key, anyTypeKey, value);
}
@Override
- public PagedConnObjectTOResult listConnObjects(
- final String key, final String anyTypeKey, final ConnObjectTOListQuery listQuery) {
+ public PagedConnObjectTOResult searchConnObjects(
+ final String key, final String anyTypeKey, final ConnObjectTOQuery query) {
+
+ Filter filter = null;
+ if (StringUtils.isNotBlank(query.getFiql())) {
+ try {
+ FilterVisitor visitor = new FilterVisitor();
+ SearchCondition<SearchBean> sc = searchContext.getCondition(query.getFiql(), SearchBean.class);
+ sc.accept(visitor);
+
+ filter = visitor.getQuery();
+ } catch (Exception e) {
+ LOG.error("Invalid FIQL expression: {}", query.getFiql(), e);
+
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSearchExpression);
+ sce.getElements().add(query.getFiql());
+ sce.getElements().add(ExceptionUtils.getRootCauseMessage(e));
+ throw sce;
+ }
+ }
- Pair<SearchResult, List<ConnObjectTO>> list = logic.listConnObjects(key, anyTypeKey,
- listQuery.getSize(), listQuery.getPagedResultsCookie(), getOrderByClauses(listQuery.getOrderBy()));
+ Pair<SearchResult, List<ConnObjectTO>> list = logic.searchConnObjects(
+ filter,
+ key,
+ anyTypeKey,
+ query.getSize(),
+ query.getPagedResultsCookie(),
+ getOrderByClauses(query.getOrderBy()));
PagedConnObjectTOResult result = new PagedConnObjectTOResult();
if (list.getLeft() != null) {
@@ -111,7 +144,7 @@ public class ResourceServiceImpl extends AbstractServiceImpl implements Resource
if (StringUtils.isNotBlank(result.getPagedResultsCookie())) {
result.setNext(builder.
replaceQueryParam(PARAM_CONNID_PAGED_RESULTS_COOKIE, result.getPagedResultsCookie()).
- replaceQueryParam(PARAM_SIZE, listQuery.getSize()).
+ replaceQueryParam(PARAM_SIZE, query.getSize()).
build());
}
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/FilterConverter.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/FilterConverter.java
new file mode 100644
index 0000000..b57c854
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/FilterConverter.java
@@ -0,0 +1,64 @@
+/*
+ * 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.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;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.identityconnectors.framework.common.objects.filter.Filter;
+
+/**
+ * Converts FIQL expressions to ConnId's {@link Filter}.
+ */
+public final class FilterConverter {
+
+ /**
+ * Parses a FIQL expression into ConnId's {@link Filter}, using {@link SyncopeFiqlParser}.
+ *
+ * @param fiql FIQL string
+ * @return {@link Filter} instance for given FIQL expression
+ */
+ public static Filter convert(final String fiql) {
+ SyncopeFiqlParser<SearchBean> parser = new SyncopeFiqlParser<>(
+ SearchBean.class, AbstractFiqlSearchConditionBuilder.CONTEXTUAL_PROPERTIES);
+
+ try {
+ FilterVisitor visitor = new FilterVisitor();
+ SearchCondition<SearchBean> sc = parser.parse(URLDecoder.decode(fiql, StandardCharsets.UTF_8));
+ sc.accept(visitor);
+
+ return visitor.getQuery();
+ } catch (Exception e) {
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSearchExpression);
+ sce.getElements().add(fiql);
+ sce.getElements().add(ExceptionUtils.getRootCauseMessage(e));
+ throw sce;
+ }
+ }
+
+ private FilterConverter() {
+ // empty constructor for static utility class
+ }
+}
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/FilterVisitor.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/FilterVisitor.java
new file mode 100644
index 0000000..8a159d4
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/FilterVisitor.java
@@ -0,0 +1,166 @@
+/*
+ * 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.persistence.api.search;
+
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import org.apache.cxf.jaxrs.ext.search.ConditionType;
+import org.apache.cxf.jaxrs.ext.search.SearchBean;
+import org.apache.cxf.jaxrs.ext.search.SearchCondition;
+import org.apache.cxf.jaxrs.ext.search.SearchUtils;
+import org.apache.cxf.jaxrs.ext.search.visitor.AbstractSearchConditionVisitor;
+import org.apache.syncope.common.lib.search.SpecialAttr;
+import org.apache.syncope.common.lib.search.SyncopeFiqlParser;
+import org.apache.syncope.common.lib.search.SyncopeFiqlSearchCondition;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.AttributeBuilder;
+import org.identityconnectors.framework.common.objects.filter.Filter;
+import org.identityconnectors.framework.common.objects.filter.FilterBuilder;
+
+public class FilterVisitor extends AbstractSearchConditionVisitor<SearchBean, Filter> {
+
+ private Filter filter;
+
+ public FilterVisitor() {
+ super(null);
+ }
+
+ private Filter visitPrimitive(final SearchCondition<SearchBean> sc) {
+ String name = getRealPropertyName(sc.getStatement().getProperty());
+ Optional<SpecialAttr> specialAttrName = SpecialAttr.fromString(name);
+
+ String value = SearchUtils.toSqlWildcardString(
+ URLDecoder.decode(sc.getStatement().getValue().toString(), StandardCharsets.UTF_8), false).
+ replaceAll("\\\\_", "_");
+ Optional<SpecialAttr> specialAttrValue = SpecialAttr.fromString(value);
+
+ ConditionType ct = sc.getConditionType();
+ if (sc instanceof SyncopeFiqlSearchCondition && sc.getConditionType() == ConditionType.CUSTOM) {
+ SyncopeFiqlSearchCondition<SearchBean> sfsc = (SyncopeFiqlSearchCondition<SearchBean>) sc;
+ switch (sfsc.getOperator()) {
+ case SyncopeFiqlParser.IEQ:
+ ct = ConditionType.EQUALS;
+ break;
+
+ case SyncopeFiqlParser.NIEQ:
+ ct = ConditionType.NOT_EQUALS;
+ break;
+
+ default:
+ throw new IllegalArgumentException(
+ String.format("Condition type %s is not supported", sfsc.getOperator()));
+ }
+ }
+
+ Attribute attr = AttributeBuilder.build(name, value);
+
+ Filter leaf;
+ switch (ct) {
+ case EQUALS:
+ case NOT_EQUALS:
+ if (!specialAttrName.isPresent()) {
+ if (specialAttrValue.isPresent() && specialAttrValue.get() == SpecialAttr.NULL) {
+ leaf = FilterBuilder.equalTo(AttributeBuilder.build(name));
+ } else if (value.indexOf('%') == -1) {
+ leaf = sc.getConditionType() == ConditionType.CUSTOM
+ ? FilterBuilder.equalsIgnoreCase(attr)
+ : FilterBuilder.equalTo(attr);
+ } else if (sc.getConditionType() != ConditionType.CUSTOM && value.startsWith("%")) {
+ leaf = FilterBuilder.endsWith(
+ AttributeBuilder.build(name, value.substring(1)));
+ } else if (sc.getConditionType() != ConditionType.CUSTOM && value.endsWith("%")) {
+ leaf = FilterBuilder.startsWith(
+ AttributeBuilder.build(name, value.substring(0, value.length() - 1)));
+ } else {
+ throw new IllegalArgumentException(
+ String.format("Unsupported search value %s", value));
+ }
+ } else {
+ throw new IllegalArgumentException(
+ String.format("Special attr name %s is not supported", specialAttrName));
+ }
+ if (ct == ConditionType.NOT_EQUALS) {
+ leaf = FilterBuilder.not(leaf);
+ }
+ break;
+
+ case GREATER_OR_EQUALS:
+ leaf = FilterBuilder.greaterThanOrEqualTo(attr);
+ break;
+
+ case GREATER_THAN:
+ leaf = FilterBuilder.greaterThan(attr);
+ break;
+
+ case LESS_OR_EQUALS:
+ leaf = FilterBuilder.lessThanOrEqualTo(attr);
+ break;
+
+ case LESS_THAN:
+ leaf = FilterBuilder.lessThan(attr);
+ break;
+
+ default:
+ throw new IllegalArgumentException(String.format("Condition type %s is not supported", ct.name()));
+ }
+
+ return leaf;
+ }
+
+ private Filter visitCompount(final SearchCondition<SearchBean> sc) {
+ List<Filter> searchConds = new ArrayList<>();
+ sc.getSearchConditions().forEach(searchCond -> {
+ searchConds.add(searchCond.getStatement() == null
+ ? visitCompount(searchCond)
+ : visitPrimitive(searchCond));
+ });
+
+ Filter compound;
+ switch (sc.getConditionType()) {
+ case AND:
+ compound = FilterBuilder.and(searchConds);
+ break;
+
+ case OR:
+ compound = FilterBuilder.or(searchConds);
+ break;
+
+ default:
+ throw new IllegalArgumentException(
+ String.format("Condition type %s is not supported", sc.getConditionType().name()));
+ }
+
+ return compound;
+ }
+
+ @Override
+ public void visit(final SearchCondition<SearchBean> sc) {
+ filter = sc.getStatement() == null
+ ? visitCompount(sc)
+ : visitPrimitive(sc);
+ }
+
+ @Override
+ public Filter getQuery() {
+ return filter;
+ }
+}
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 7a1a9d4..616a195 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
@@ -98,13 +98,18 @@ public class SearchCondVisitor extends AbstractSearchConditionVisitor<SearchBean
ConditionType ct = sc.getConditionType();
if (sc instanceof SyncopeFiqlSearchCondition && sc.getConditionType() == ConditionType.CUSTOM) {
SyncopeFiqlSearchCondition<SearchBean> sfsc = (SyncopeFiqlSearchCondition<SearchBean>) sc;
- if (SyncopeFiqlParser.IEQ.equals(sfsc.getOperator())) {
- ct = ConditionType.EQUALS;
- } else if (SyncopeFiqlParser.NIEQ.equals(sfsc.getOperator())) {
- ct = ConditionType.NOT_EQUALS;
- } else {
- throw new IllegalArgumentException(
- String.format("Condition type %s is not supported", sfsc.getOperator()));
+ switch (sfsc.getOperator()) {
+ case SyncopeFiqlParser.IEQ:
+ ct = ConditionType.EQUALS;
+ break;
+
+ case SyncopeFiqlParser.NIEQ:
+ ct = ConditionType.NOT_EQUALS;
+ break;
+
+ default:
+ throw new IllegalArgumentException(
+ String.format("Condition type %s is not supported", sfsc.getOperator()));
}
}
@@ -252,9 +257,9 @@ public class SearchCondVisitor extends AbstractSearchConditionVisitor<SearchBean
private SearchCond visitCompount(final SearchCondition<SearchBean> sc) {
List<SearchCond> searchConds = new ArrayList<>();
- sc.getSearchConditions().forEach(searchCondition -> searchConds.add(searchCondition.getStatement() == null
- ? visitCompount(searchCondition)
- : visitPrimitive(searchCondition)));
+ sc.getSearchConditions().forEach(searchCond -> searchConds.add(searchCond.getStatement() == null
+ ? visitCompount(searchCond)
+ : visitPrimitive(searchCond)));
SearchCond compound;
switch (sc.getConditionType()) {
@@ -285,5 +290,4 @@ public class SearchCondVisitor extends AbstractSearchConditionVisitor<SearchBean
public SearchCond getQuery() {
return searchCond;
}
-
}
diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/FilterConverterTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/FilterConverterTest.java
new file mode 100644
index 0000000..40c9b69
--- /dev/null
+++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/FilterConverterTest.java
@@ -0,0 +1,289 @@
+/*
+ * 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.persistence.api.search;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.List;
+import java.util.ListIterator;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.search.SpecialAttr;
+import org.apache.syncope.common.lib.search.ConnObjectTOFiqlSearchConditionBuilder;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.identityconnectors.framework.common.objects.AttributeBuilder;
+import org.identityconnectors.framework.common.objects.filter.AndFilter;
+import org.identityconnectors.framework.common.objects.filter.Filter;
+import org.identityconnectors.framework.common.objects.filter.FilterBuilder;
+import org.identityconnectors.framework.common.objects.filter.NotFilter;
+import org.identityconnectors.framework.common.objects.filter.OrFilter;
+import org.junit.jupiter.api.Test;
+
+public class FilterConverterTest {
+
+ private boolean equals(final Filter filter1, final Filter filter2) {
+ return EqualsBuilder.reflectionEquals(filter1, filter2);
+ }
+
+ private boolean equals(final List<Filter> filters1, final List<Filter> filters2) {
+ ListIterator<Filter> e1 = filters1.listIterator();
+ ListIterator<Filter> e2 = filters2.listIterator();
+ while (e1.hasNext() && e2.hasNext()) {
+ Filter o1 = e1.next();
+ Filter o2 = e2.next();
+ if (!equals(o1, o2)) {
+ return false;
+ }
+ }
+ return !(e1.hasNext() || e2.hasNext());
+ }
+
+ @Test
+ public void eq() {
+ String fiql = new ConnObjectTOFiqlSearchConditionBuilder().is("username").equalTo("rossini").query();
+ assertEquals("username==rossini", fiql);
+
+ Filter filter = FilterBuilder.equalTo(AttributeBuilder.build("username", "rossini"));
+
+ assertTrue(equals(filter, FilterConverter.convert(fiql)));
+ }
+
+ @Test
+ public void ieq() {
+ String fiql = new ConnObjectTOFiqlSearchConditionBuilder().is("username").equalToIgnoreCase("rossini").query();
+ assertEquals("username=~rossini", fiql);
+
+ Filter filter = FilterBuilder.equalsIgnoreCase(AttributeBuilder.build("username", "rossini"));
+
+ assertTrue(equals(filter, FilterConverter.convert(fiql)));
+ }
+
+ @Test
+ public void nieq() {
+ String fiql = new ConnObjectTOFiqlSearchConditionBuilder().is("username").notEqualTolIgnoreCase("rossini").
+ query();
+ assertEquals("username!~rossini", fiql);
+
+ Filter filter = FilterBuilder.not(
+ FilterBuilder.equalsIgnoreCase(AttributeBuilder.build("username", "rossini")));
+ assertTrue(filter instanceof NotFilter);
+
+ Filter converted = FilterConverter.convert(fiql);
+ assertTrue(converted instanceof NotFilter);
+
+ assertTrue(equals(
+ ((NotFilter) filter).getFilter(), ((NotFilter) converted).getFilter()));
+ }
+
+ @Test
+ public void like() {
+ String fiql = new ConnObjectTOFiqlSearchConditionBuilder().is("username").equalTo("ros*").query();
+ assertEquals("username==ros*", fiql);
+
+ Filter filter = FilterBuilder.startsWith(AttributeBuilder.build("username", "ros"));
+
+ assertTrue(equals(filter, FilterConverter.convert(fiql)));
+
+ fiql = new ConnObjectTOFiqlSearchConditionBuilder().is("username").equalTo("*ini").query();
+ assertEquals("username==*ini", fiql);
+
+ filter = FilterBuilder.endsWith(AttributeBuilder.build("username", "ini"));
+
+ assertTrue(equals(filter, FilterConverter.convert(fiql)));
+
+ fiql = new ConnObjectTOFiqlSearchConditionBuilder().is("username").equalTo("r*ini").query();
+ assertEquals("username==r*ini", fiql);
+
+ try {
+ FilterConverter.convert(fiql);
+ fail();
+ } catch (SyncopeClientException e) {
+ assertEquals(ClientExceptionType.InvalidSearchExpression, e.getType());
+ }
+ }
+
+ @Test
+ public void ilike() {
+ String fiql = new ConnObjectTOFiqlSearchConditionBuilder().is("username").equalToIgnoreCase("ros*").query();
+ assertEquals("username=~ros*", fiql);
+
+ try {
+ FilterConverter.convert(fiql);
+ fail();
+ } catch (SyncopeClientException e) {
+ assertEquals(ClientExceptionType.InvalidSearchExpression, e.getType());
+ }
+ }
+
+ @Test
+ public void nilike() {
+ String fiql = new ConnObjectTOFiqlSearchConditionBuilder().is("username").notEqualTolIgnoreCase("ros*").query();
+ assertEquals("username!~ros*", fiql);
+
+ try {
+ FilterConverter.convert(fiql);
+ fail();
+ } catch (SyncopeClientException e) {
+ assertEquals(ClientExceptionType.InvalidSearchExpression, e.getType());
+ }
+ }
+
+ @Test
+ public void isNull() {
+ String fiql = new ConnObjectTOFiqlSearchConditionBuilder().is("loginDate").nullValue().query();
+ assertEquals("loginDate==" + SpecialAttr.NULL, fiql);
+
+ Filter filter = FilterBuilder.equalTo(AttributeBuilder.build("loginDate"));
+
+ assertTrue(equals(filter, FilterConverter.convert(fiql)));
+ }
+
+ @Test
+ public void isNotNull() {
+ String fiql = new ConnObjectTOFiqlSearchConditionBuilder().is("loginDate").notNullValue().query();
+ assertEquals("loginDate!=" + SpecialAttr.NULL, fiql);
+
+ Filter filter = FilterBuilder.not(FilterBuilder.equalTo(AttributeBuilder.build("loginDate")));
+ assertTrue(filter instanceof NotFilter);
+
+ Filter converted = FilterConverter.convert(fiql);
+ assertTrue(converted instanceof NotFilter);
+
+ assertTrue(equals(
+ ((NotFilter) filter).getFilter(), ((NotFilter) converted).getFilter()));
+ }
+
+ @Test
+ public void inDynRealms() {
+ try {
+ new ConnObjectTOFiqlSearchConditionBuilder().inDynRealms("realm").query();
+ fail();
+ } catch (UnsupportedOperationException e) {
+ assertNotNull(e);
+ }
+
+ try {
+ FilterConverter.convert(SpecialAttr.DYNREALMS + "==realm");
+ fail();
+ } catch (SyncopeClientException e) {
+ assertEquals(ClientExceptionType.InvalidSearchExpression, e.getType());
+ }
+ }
+
+ @Test
+ public void notInDynRealms() {
+ try {
+ new ConnObjectTOFiqlSearchConditionBuilder().notInDynRealms("realm").query();
+ fail();
+ } catch (UnsupportedOperationException e) {
+ assertNotNull(e);
+ }
+
+ try {
+ FilterConverter.convert(SpecialAttr.DYNREALMS + "!=realm");
+ fail();
+ } catch (SyncopeClientException e) {
+ assertEquals(ClientExceptionType.InvalidSearchExpression, e.getType());
+ }
+ }
+
+ @Test
+ public void hasResources() {
+ try {
+ new ConnObjectTOFiqlSearchConditionBuilder().hasResources("resource").query();
+ fail();
+ } catch (UnsupportedOperationException e) {
+ assertNotNull(e);
+ }
+
+ try {
+ FilterConverter.convert(SpecialAttr.RESOURCES + "==resource");
+ fail();
+ } catch (SyncopeClientException e) {
+ assertEquals(ClientExceptionType.InvalidSearchExpression, e.getType());
+ }
+ }
+
+ @Test
+ public void hasNotResources() {
+ try {
+ new ConnObjectTOFiqlSearchConditionBuilder().hasNotResources("resource").query();
+ fail();
+ } catch (UnsupportedOperationException e) {
+ assertNotNull(e);
+ }
+
+ try {
+ FilterConverter.convert(SpecialAttr.RESOURCES + "!=resource");
+ fail();
+ } catch (SyncopeClientException e) {
+ assertEquals(ClientExceptionType.InvalidSearchExpression, e.getType());
+ }
+ }
+
+ @Test
+ public void and() {
+ String fiql = new ConnObjectTOFiqlSearchConditionBuilder().
+ is("fullname").equalTo("ro*").and("fullname").equalTo("*i").query();
+ assertEquals("fullname==ro*;fullname==*i", fiql);
+
+ Filter filter1 = FilterBuilder.startsWith(AttributeBuilder.build("fullname", "ro"));
+ Filter filter2 = FilterBuilder.endsWith(AttributeBuilder.build("fullname", "i"));
+
+ Filter filter = FilterBuilder.and(filter1, filter2);
+ assertTrue(filter instanceof AndFilter);
+
+ Filter converted = FilterConverter.convert(fiql);
+ assertTrue(converted instanceof AndFilter);
+
+ assertTrue(equals(
+ (List<Filter>) ((AndFilter) filter).getFilters(), (List<Filter>) ((AndFilter) converted).getFilters()));
+ }
+
+ @Test
+ public void or() {
+ String fiql = new ConnObjectTOFiqlSearchConditionBuilder().
+ is("fullname").equalTo("ro*").or("fullname").equalTo("*i").query();
+ assertEquals("fullname==ro*,fullname==*i", fiql);
+
+ Filter filter1 = FilterBuilder.startsWith(AttributeBuilder.build("fullname", "ro"));
+ Filter filter2 = FilterBuilder.endsWith(AttributeBuilder.build("fullname", "i"));
+
+ Filter filter = FilterBuilder.or(filter1, filter2);
+ assertTrue(filter instanceof OrFilter);
+
+ Filter converted = FilterConverter.convert(fiql);
+ assertTrue(converted instanceof OrFilter);
+
+ assertTrue(equals(
+ (List<Filter>) ((OrFilter) filter).getFilters(), (List<Filter>) ((OrFilter) converted).getFilters()));
+ }
+
+ @Test
+ public void issueSYNCOPE1223() {
+ String fiql = new ConnObjectTOFiqlSearchConditionBuilder().is("ctype").equalTo("ou=sample%252Co=isp").query();
+
+ Filter filter = FilterBuilder.equalTo(AttributeBuilder.build("ctype", "ou=sample,o=isp"));
+
+ assertTrue(equals(filter, FilterConverter.convert(fiql)));
+ }
+}
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LinkedAccountITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LinkedAccountITCase.java
index f02f5cd..a04a07f 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LinkedAccountITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LinkedAccountITCase.java
@@ -30,8 +30,6 @@ import java.util.List;
import java.util.Optional;
import java.util.UUID;
import javax.naming.NamingException;
-import javax.naming.directory.Attributes;
-import javax.naming.ldap.LdapContext;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@@ -44,6 +42,7 @@ import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.policy.PullPolicyTO;
+import org.apache.syncope.common.lib.to.ConnObjectTO;
import org.apache.syncope.common.lib.to.ExecTO;
import org.apache.syncope.common.lib.to.ImplementationTO;
import org.apache.syncope.common.lib.to.LinkedAccountTO;
@@ -102,15 +101,11 @@ public class LinkedAccountITCase extends AbstractITCase {
assertEquals(ResourceOperation.CREATE, tasks.getResult().get(0).getOperation());
assertEquals(ExecStatus.SUCCESS.name(), tasks.getResult().get(0).getLatestExecStatus());
- LdapContext ldapObj = (LdapContext) getLdapRemoteObject(
- RESOURCE_LDAP_ADMIN_DN, RESOURCE_LDAP_ADMIN_PWD, connObjectKeyValue);
+ ConnObjectTO ldapObj = resourceService.readConnObject(
+ RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), connObjectKeyValue);
assertNotNull(ldapObj);
-
- Attributes ldapAttrs = ldapObj.getAttributes("");
- assertEquals(
- user.getPlainAttr("email").get().getValues().get(0),
- ldapAttrs.get("mail").getAll().next().toString());
- assertEquals("LINKED_SURNAME", ldapAttrs.get("sn").getAll().next().toString());
+ assertEquals(user.getPlainAttr("email").get().getValues(), ldapObj.getAttr("mail").get().getValues());
+ assertEquals("LINKED_SURNAME", ldapObj.getAttr("sn").get().getValues().get(0));
// 3. remove linked account from user
UserUR userUR = new UserUR();
@@ -125,12 +120,11 @@ public class LinkedAccountITCase extends AbstractITCase {
assertEquals(1, user.getLinkedAccounts().size());
// 4 verify that account was updated on resource
- ldapObj = (LdapContext) getLdapRemoteObject(RESOURCE_LDAP_ADMIN_DN, RESOURCE_LDAP_ADMIN_PWD, connObjectKeyValue);
+ ldapObj = resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), connObjectKeyValue);
assertNotNull(ldapObj);
- ldapAttrs = ldapObj.getAttributes("");
- assertEquals("UPDATED_EMAIL@syncope.apache.org", ldapAttrs.get("mail").getAll().next().toString());
- assertEquals("UPDATED_SURNAME", ldapAttrs.get("sn").getAll().next().toString());
+ assertTrue(ldapObj.getAttr("mail").get().getValues().contains("UPDATED_EMAIL@syncope.apache.org"));
+ assertEquals("UPDATED_SURNAME", ldapObj.getAttr("sn").get().getValues().get(0));
// 5. remove linked account from user
userUR = new UserUR();
@@ -196,15 +190,11 @@ public class LinkedAccountITCase extends AbstractITCase {
assertEquals(ResourceOperation.CREATE, tasks.getResult().get(0).getOperation());
assertEquals(ExecStatus.SUCCESS.name(), tasks.getResult().get(0).getLatestExecStatus());
- LdapContext ldapObj = (LdapContext) getLdapRemoteObject(
- RESOURCE_LDAP_ADMIN_DN, RESOURCE_LDAP_ADMIN_PWD, connObjectKeyValue);
+ ConnObjectTO ldapObj = resourceService.readConnObject(
+ RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), connObjectKeyValue);
assertNotNull(ldapObj);
-
- Attributes ldapAttrs = ldapObj.getAttributes("");
- assertEquals(
- user.getPlainAttr("email").get().getValues().get(0),
- ldapAttrs.get("mail").getAll().next().toString());
- assertEquals("LINKED_SURNAME", ldapAttrs.get("sn").getAll().next().toString());
+ assertEquals(user.getPlainAttr("email").get().getValues(), ldapObj.getAttr("mail").get().getValues());
+ assertEquals("LINKED_SURNAME", ldapObj.getAttr("sn").get().getValues().get(0));
}
@Test
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ResourceITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ResourceITCase.java
index 74e8473..d1d8826 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ResourceITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ResourceITCase.java
@@ -27,26 +27,19 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
-import java.util.stream.Collectors;
import javax.ws.rs.core.Response;
import org.apache.commons.lang3.SerializationUtils;
-import org.apache.syncope.client.console.commons.ConnIdSpecialName;
import org.apache.syncope.client.lib.SyncopeClient;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.request.AnyObjectCR;
-import org.apache.syncope.common.lib.request.GroupCR;
import org.apache.syncope.common.lib.to.AnyObjectTO;
-import org.apache.syncope.common.lib.to.GroupTO;
import org.apache.syncope.common.lib.to.ItemTO;
import org.apache.syncope.common.lib.to.MappingTO;
import org.apache.syncope.common.lib.to.OrgUnitTO;
-import org.apache.syncope.common.lib.to.PagedConnObjectTOResult;
import org.apache.syncope.common.lib.to.ProvisionTO;
import org.apache.syncope.common.lib.to.ResourceHistoryConfTO;
import org.apache.syncope.common.lib.to.ResourceTO;
@@ -58,7 +51,6 @@ import org.apache.syncope.common.lib.types.EntityViolationType;
import org.apache.syncope.common.lib.types.IdMImplementationType;
import org.apache.syncope.common.lib.types.MappingPurpose;
import org.apache.syncope.common.lib.types.TraceLevel;
-import org.apache.syncope.common.rest.api.beans.ConnObjectTOListQuery;
import org.apache.syncope.common.rest.api.service.ResourceService;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.apache.syncope.fit.AbstractITCase;
@@ -518,56 +510,6 @@ public class ResourceITCase extends AbstractITCase {
}
@Test
- public void listConnObjects() {
- List<String> groupKeys = new ArrayList<>();
- for (int i = 0; i < 10; i++) {
- GroupCR groupCR = GroupITCase.getSample("group");
- groupCR.getResources().add(RESOURCE_NAME_LDAP);
- GroupTO group = createGroup(groupCR).getEntity();
- groupKeys.add(group.getKey());
- }
-
- int totalRead = 0;
- Set<String> read = new HashSet<>();
- try {
- ConnObjectTOListQuery.Builder builder = new ConnObjectTOListQuery.Builder().size(10);
- PagedConnObjectTOResult list;
- do {
- list = null;
-
- boolean succeeded = false;
- // needed because ApacheDS seems to randomly fail when searching with cookie
- for (int i = 0; i < 5 && !succeeded; i++) {
- try {
- list = resourceService.listConnObjects(
- RESOURCE_NAME_LDAP,
- AnyTypeKind.GROUP.name(),
- builder.build());
- succeeded = true;
- } catch (SyncopeClientException e) {
- assertEquals(ClientExceptionType.ConnectorException, e.getType());
- }
- }
- assertNotNull(list);
-
- totalRead += list.getResult().size();
- read.addAll(list.getResult().stream().
- map(input -> input.getAttr(ConnIdSpecialName.NAME).get().getValues().get(0)).
- collect(Collectors.toList()));
-
- if (list.getPagedResultsCookie() != null) {
- builder.pagedResultsCookie(list.getPagedResultsCookie());
- }
- } while (list.getPagedResultsCookie() != null);
-
- assertEquals(totalRead, read.size());
- assertTrue(totalRead >= 10);
- } finally {
- groupKeys.forEach(key -> groupService.delete(key));
- }
- }
-
- @Test
public void history() {
List<ResourceHistoryConfTO> history = resourceHistoryService.list(RESOURCE_NAME_LDAP);
assertNotNull(history);
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 35b298d..347e0b7 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
@@ -25,8 +25,14 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
import javax.ws.rs.core.Response;
import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.syncope.client.console.commons.ConnIdSpecialName;
import org.apache.syncope.client.lib.SyncopeClient;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.SyncopeConstants;
@@ -39,18 +45,22 @@ import org.apache.syncope.common.lib.request.UserUR;
import org.apache.syncope.common.lib.request.AttrPatch;
import org.apache.syncope.common.lib.to.AnyObjectTO;
import org.apache.syncope.common.lib.to.AnyTypeTO;
+import org.apache.syncope.common.lib.to.ConnObjectTO;
import org.apache.syncope.common.lib.to.PagedResult;
import org.apache.syncope.common.lib.to.GroupTO;
import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.syncope.common.lib.to.PagedConnObjectTOResult;
import org.apache.syncope.common.lib.to.RoleTO;
import org.apache.syncope.common.lib.to.UserTO;
import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.common.lib.types.ClientExceptionType;
import org.apache.syncope.common.rest.api.beans.AnyQuery;
+import org.apache.syncope.common.rest.api.beans.ConnObjectTOQuery;
import org.apache.syncope.common.rest.api.service.RoleService;
import org.apache.syncope.fit.AbstractITCase;
import org.apache.syncope.fit.ElasticsearchDetector;
import org.junit.jupiter.api.Assertions;
+import org.identityconnectors.framework.common.objects.Name;
import org.junit.jupiter.api.Test;
public class SearchITCase extends AbstractITCase {
@@ -75,7 +85,8 @@ public class SearchITCase extends AbstractITCase {
assertNotNull(matchingUsers);
assertFalse(matchingUsers.getResult().isEmpty());
- assertEquals(2, matchingUsers.getResult().stream().filter(user -> "74cd8ece-715a-44a4-a736-e17b46c4e7e6".equals(user.getKey())
+ assertEquals(2, matchingUsers.getResult().stream().filter(user -> "74cd8ece-715a-44a4-a736-e17b46c4e7e6".equals(
+ user.getKey())
|| "b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee".equals(user.getKey())).count());
}
@@ -445,6 +456,113 @@ public class SearchITCase extends AbstractITCase {
}
@Test
+ public void searchConnObjectsBrowsePagedResult() {
+ List<String> groupKeys = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ GroupCR groupCR = GroupITCase.getSample("group");
+ groupCR.getResources().add(RESOURCE_NAME_LDAP);
+ GroupTO group = createGroup(groupCR).getEntity();
+ groupKeys.add(group.getKey());
+ }
+
+ int totalRead = 0;
+ Set<String> read = new HashSet<>();
+ try {
+ // 1. first search with no filters
+ ConnObjectTOQuery.Builder builder = new ConnObjectTOQuery.Builder().size(10);
+ PagedConnObjectTOResult matches;
+ do {
+ matches = null;
+
+ boolean succeeded = false;
+ // needed because ApacheDS seems to randomly fail when searching with cookie
+ for (int i = 0; i < 5 && !succeeded; i++) {
+ try {
+ matches = resourceService.searchConnObjects(
+ RESOURCE_NAME_LDAP,
+ AnyTypeKind.GROUP.name(),
+ builder.build());
+ succeeded = true;
+ } catch (SyncopeClientException e) {
+ assertEquals(ClientExceptionType.ConnectorException, e.getType());
+ }
+ }
+ assertNotNull(matches);
+
+ totalRead += matches.getResult().size();
+ read.addAll(matches.getResult().stream().
+ map(input -> input.getAttr(ConnIdSpecialName.NAME).get().getValues().get(0)).
+ collect(Collectors.toList()));
+
+ if (matches.getPagedResultsCookie() != null) {
+ builder.pagedResultsCookie(matches.getPagedResultsCookie());
+ }
+ } while (matches.getPagedResultsCookie() != null);
+
+ assertEquals(totalRead, read.size());
+ assertTrue(totalRead >= 10);
+ } finally {
+ groupKeys.forEach(key -> {
+ groupService.delete(key);
+ });
+ }
+ }
+
+ @Test
+ public void searchConnObjectsWithFilter() {
+ ConnObjectTO user = resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), "pullFromLDAP");
+ assertNotNull(user);
+
+ PagedConnObjectTOResult matches = resourceService.searchConnObjects(
+ RESOURCE_NAME_LDAP,
+ AnyTypeKind.USER.name(),
+ new ConnObjectTOQuery.Builder().size(100).fiql(
+ SyncopeClient.getConnObjectTOFiqlSearchConditionBuilder().
+ is("givenName").equalTo("pullFromLDAP").query()).build());
+ assertTrue(matches.getResult().contains(user));
+
+ matches = resourceService.searchConnObjects(
+ RESOURCE_NAME_LDAP,
+ AnyTypeKind.USER.name(),
+ new ConnObjectTOQuery.Builder().size(100).fiql(
+ SyncopeClient.getConnObjectTOFiqlSearchConditionBuilder().
+ is("mail").equalTo("pullFromLDAP*").query()).build());
+ assertTrue(matches.getResult().contains(user));
+
+ matches = resourceService.searchConnObjects(
+ RESOURCE_NAME_LDAP,
+ AnyTypeKind.USER.name(),
+ new ConnObjectTOQuery.Builder().size(100).fiql(
+ SyncopeClient.getConnObjectTOFiqlSearchConditionBuilder().
+ is("mail").equalTo("*@syncope.apache.org").query()).build());
+ assertTrue(matches.getResult().contains(user));
+
+ matches = resourceService.searchConnObjects(
+ RESOURCE_NAME_LDAP,
+ AnyTypeKind.USER.name(),
+ new ConnObjectTOQuery.Builder().size(100).fiql(
+ SyncopeClient.getConnObjectTOFiqlSearchConditionBuilder().
+ is("givenName").equalToIgnoreCase("pullfromldap").query()).build());
+ assertTrue(matches.getResult().contains(user));
+
+ matches = resourceService.searchConnObjects(
+ RESOURCE_NAME_LDAP,
+ AnyTypeKind.USER.name(),
+ new ConnObjectTOQuery.Builder().size(100).fiql(
+ SyncopeClient.getConnObjectTOFiqlSearchConditionBuilder().
+ is(Name.NAME).equalTo("uid=pullFromLDAP%252Cou=people%252Co=isp").query()).build());
+ assertTrue(matches.getResult().contains(user));
+
+ matches = resourceService.searchConnObjects(
+ RESOURCE_NAME_LDAP,
+ AnyTypeKind.USER.name(),
+ new ConnObjectTOQuery.Builder().size(100).fiql(
+ SyncopeClient.getConnObjectTOFiqlSearchConditionBuilder().
+ is("givenName").notEqualTo("pullFromLDAP").query()).build());
+ assertFalse(matches.getResult().contains(user));
+ }
+
+ @Test
public void issueSYNCOPE768() {
int usersWithNullable = userService.search(new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
fiql(SyncopeClient.getUserSearchConditionBuilder().is("ctype").nullValue().query()).build()).
@@ -534,7 +652,7 @@ public class SearchITCase extends AbstractITCase {
req.getPlainAttrs().add(new AttrPatch.Builder(attr("ctype", "ou=sample,o=isp")).build());
userService.update(req);
- if (ElasticsearchDetector.isElasticSearchEnabled(syncopeService)) {
+ if (ElasticsearchDetector.isElasticSearchEnabled(syncopeService)) {
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {