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 2021/01/25 16:56:42 UTC
[syncope] branch master updated: [SYNCOPE-1610] Refactoring
ReconQuery to work with FIQL (#234)
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 dd1c82a [SYNCOPE-1610] Refactoring ReconQuery to work with FIQL (#234)
dd1c82a is described below
commit dd1c82adc6cfd340f198c6123971bff27a826562
Author: Francesco Chicchiriccò <il...@users.noreply.github.com>
AuthorDate: Mon Jan 25 17:12:17 2021 +0100
[SYNCOPE-1610] Refactoring ReconQuery to work with FIQL (#234)
---
.../console/panels/ConnObjectListViewPanel.java | 60 ++--
.../syncope/client/console/panels/ConnObjects.java | 6 +-
.../console/panels/LinkedAccountModalPanel.java | 5 +-
.../client/console/status/ReconStatusUtils.java | 5 +-
.../client/console/status/ReconTaskPanel.java | 7 +-
.../any/MergeLinkedAccountsWizardBuilder.java | 78 +++--
.../client/console/SyncopeConsoleSession.java | 70 +++-
.../console/SyncopeConsoleApplicationTest.java | 8 +-
.../syncope/common/rest/api/beans/ReconQuery.java | 16 +-
.../rest/api/service/ReconciliationService.java | 6 +-
.../apache/syncope/common/lib/to/ConnObjectTO.java | 12 +
.../syncope/core/logic/ReconciliationLogic.java | 352 +++++++++++++--------
.../apache/syncope/core/logic/ResourceLogic.java | 25 +-
.../cxf/service/ReconciliationServiceImpl.java | 97 ++++--
.../persistence/api/dao/PushCorrelationRule.java | 26 +-
.../jpa/dao/DefaultPushCorrelationRule.java | 61 +++-
.../api/propagation/PropagationReporter.java | 2 +
...ecutor.java => ConstantReconFilterBuilder.java} | 26 +-
...ecutor.java => KeyValueReconFilterBuilder.java} | 31 +-
.../api/pushpull/SyncopeSinglePullExecutor.java | 3 +-
.../java/data/AbstractAnyDataBinder.java | 44 ++-
.../AbstractPropagationTaskExecutor.java | 9 +
.../propagation/DefaultPropagationReporter.java | 5 +-
.../PriorityPropagationTaskExecutor.java | 2 +-
.../java/pushpull/OutboundMatcher.java | 6 +
.../java/pushpull/SinglePullJobDelegate.java | 25 +-
.../provisioning/java/utils/ConnObjectUtils.java | 19 +-
.../syncope/fit/core/PropagationTaskITCase.java | 9 +-
.../syncope/fit/core/ReconciliationITCase.java | 10 +-
29 files changed, 680 insertions(+), 345 deletions(-)
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjectListViewPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjectListViewPanel.java
index 3a39acb..afe3278 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjectListViewPanel.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjectListViewPanel.java
@@ -27,6 +27,8 @@ import java.util.List;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.client.console.SyncopeConsoleSession;
+import org.apache.syncope.client.console.pages.BasePage;
import org.apache.syncope.client.console.panels.ListViewPanel.ListViewReload;
import org.apache.syncope.client.console.panels.search.AbstractSearchPanel;
import org.apache.syncope.client.console.panels.search.ConnObjectSearchPanel;
@@ -70,11 +72,15 @@ import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.StringResourceModel;
import org.apache.wicket.model.util.ListModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public abstract class ConnObjectListViewPanel extends Panel {
private static final long serialVersionUID = 4986172040062752781L;
+ private static final Logger LOG = LoggerFactory.getLogger(ConnObjectListViewPanel.class);
+
private static final int SIZE = 10;
private static final String STATUS = "Status";
@@ -139,9 +145,9 @@ public abstract class ConnObjectListViewPanel extends Panel {
accordion.setOutputMarkupId(true);
add(accordion.setEnabled(true).setVisible(true));
- final List<ConnObjectTO> listOfItems = reloadItems(resource.getKey(), anyType, null, null);
+ List<ConnObjectTO> listOfItems = reloadItems(resource.getKey(), anyType, null, null);
- final ListViewPanel.Builder<ConnObjectTO> builder = new ListViewPanel.Builder<ConnObjectTO>(
+ ListViewPanel.Builder<ConnObjectTO> builder = new ListViewPanel.Builder<ConnObjectTO>(
ConnObjectTO.class, pageRef) {
private static final long serialVersionUID = -8251750413385566738L;
@@ -149,15 +155,21 @@ public abstract class ConnObjectListViewPanel extends Panel {
@Override
protected Component getValueComponent(final String key, final ConnObjectTO bean) {
if (StringUtils.equals(key, STATUS)) {
- ReconStatus status = ReconciliationRestClient.status(
- new ReconQuery.Builder(anyType, resource.getKey()).
- connObjectKeyValue(bean.getAttr(ConnIdSpecialName.UID).get().getValues().get(0)).
- build());
+ ReconStatus status;
+ try {
+ status = ReconciliationRestClient.status(
+ new ReconQuery.Builder(anyType, resource.getKey()).fiql(bean.getFiql()).build());
+ } catch (Exception e) {
+ LOG.error("While requesting for reconciliation status of {} {} with FIQL '{}'",
+ anyType, resource.getKey(), bean.getFiql(), e);
+
+ status = new ReconStatus();
+ }
return status.getOnSyncope() == null
? StatusUtils.getLabel("field", "notfound icon", "Not found", Constants.NOT_FOUND_ICON)
: new Label("field", Model.of()).add(new PopoverBehavior(
- Model.<String>of(),
+ Model.of(),
Model.of(status.getAnyKey()),
new PopoverConfig().
withTitle(status.getMatchType() == MatchType.LINKED_ACCOUNT
@@ -214,19 +226,25 @@ public abstract class ConnObjectListViewPanel extends Panel {
@Override
public void onClick(final AjaxRequestTarget target, final ConnObjectTO modelObject) {
- String connObjectKeyValue = modelObject.getAttr(ConnIdSpecialName.UID).get().getValues().get(0);
- ReconStatus status = ReconciliationRestClient.status(
- new ReconQuery.Builder(anyType, resource.getKey()).
- connObjectKeyValue(connObjectKeyValue).build());
-
- pullConnObject(
- connObjectKeyValue,
- target,
- resource.getKey(),
- anyType,
- status.getRealm(),
- StringUtils.isNotBlank(status.getAnyKey()),
- pageRef);
+ try {
+ ReconStatus status = ReconciliationRestClient.status(
+ new ReconQuery.Builder(anyType, resource.getKey()).fiql(modelObject.getFiql()).build());
+
+ pullConnObject(
+ modelObject.getFiql(),
+ target,
+ resource.getKey(),
+ anyType,
+ status.getRealm(),
+ StringUtils.isNotBlank(status.getAnyKey()),
+ pageRef);
+ } catch (Exception e) {
+ LOG.error("While puling single object {} {} with FIQL '{}'",
+ anyType, resource.getKey(), modelObject.getFiql(), e);
+
+ SyncopeConsoleSession.get().onException(e);
+ ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target);
+ }
}
}, ActionLink.ActionType.RECONCILIATION_PULL, IdRepoEntitlement.TASK_EXECUTE);
@@ -272,7 +290,7 @@ public abstract class ConnObjectListViewPanel extends Panel {
protected abstract void viewConnObject(ConnObjectTO connObjectTO, AjaxRequestTarget target);
protected abstract void pullConnObject(
- String connObjectTO,
+ String fiql,
AjaxRequestTarget target,
String resource,
String anyType,
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjects.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjects.java
index 47350fb..4af0ea9 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjects.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ConnObjects.java
@@ -128,13 +128,14 @@ public class ConnObjects extends Panel implements ModalPanel {
@Override
protected void pullConnObject(
- final String connObjectKeyValue,
+ final String fiql,
final AjaxRequestTarget target,
final String resource,
final String anyType,
final String realm,
final boolean isOnSyncope,
final PageReference pageRef) {
+
anyTypes.setEnabled(false);
target.add(anyTypes);
@@ -146,13 +147,12 @@ public class ConnObjects extends Panel implements ModalPanel {
pullTaskTO,
anyType,
null,
- connObjectKeyValue,
+ fiql,
isOnSyncope,
connObjects,
pageRef),
target
);
}
-
}
}
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/LinkedAccountModalPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/LinkedAccountModalPanel.java
index 83f2db2..3e1b62b 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/LinkedAccountModalPanel.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/LinkedAccountModalPanel.java
@@ -39,6 +39,7 @@ import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink;
import org.apache.syncope.client.console.wicket.markup.html.form.ActionLinksTogglePanel;
import org.apache.syncope.client.console.wizards.WizardMgtPanel;
import org.apache.syncope.client.console.wizards.any.LinkedAccountWizardBuilder;
+import org.apache.syncope.client.ui.commons.ConnIdSpecialName;
import org.apache.syncope.client.ui.commons.Constants;
import org.apache.syncope.client.ui.commons.panels.ModalPanel;
import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
@@ -272,7 +273,7 @@ public class LinkedAccountModalPanel extends Panel implements ModalPanel {
new PushTaskTO(),
model.getObject().getType(),
null,
- linkedAccountTO.getConnObjectKeyValue(),
+ ConnIdSpecialName.UID + "==" + linkedAccountTO.getConnObjectKeyValue(),
true,
mlp,
pageRef),
@@ -298,7 +299,7 @@ public class LinkedAccountModalPanel extends Panel implements ModalPanel {
new PullTaskTO(),
model.getObject().getType(),
null,
- linkedAccountTO.getConnObjectKeyValue(),
+ ConnIdSpecialName.UID + "==" + linkedAccountTO.getConnObjectKeyValue(),
true,
mlp,
pageRef),
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconStatusUtils.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconStatusUtils.java
index 9fba8d5..81d7879 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconStatusUtils.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconStatusUtils.java
@@ -26,6 +26,7 @@ import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.syncope.client.console.rest.ReconciliationRestClient;
+import org.apache.syncope.client.ui.commons.ConnIdSpecialName;
import org.apache.syncope.common.lib.to.ReconStatus;
import org.apache.syncope.common.rest.api.beans.ReconQuery;
import org.slf4j.Logger;
@@ -42,8 +43,8 @@ public final class ReconStatusUtils implements Serializable {
ReconStatus result = null;
try {
- result = ReconciliationRestClient.status(
- new ReconQuery.Builder(anyTypeKey, resource).connObjectKeyValue(connObjectKeyValue).build());
+ result = ReconciliationRestClient.status(new ReconQuery.Builder(anyTypeKey, resource).
+ fiql(ConnIdSpecialName.UID + "==" + connObjectKeyValue).build());
} catch (Exception e) {
LOG.warn("Unexpected error for {} {} on {}", anyTypeKey, connObjectKeyValue, resource, e);
}
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 14e6cf4..f6bce2f 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
@@ -95,6 +95,7 @@ public class ReconTaskPanel extends MultilevelPanel.SecondLevel {
final boolean isOnSyncope,
final MultilevelPanel multiLevelPanelRef,
final PageReference pageRef) {
+
this(resource, taskTO, anyType, anyKey, null, isOnSyncope, multiLevelPanelRef, pageRef);
}
@@ -103,7 +104,7 @@ public class ReconTaskPanel extends MultilevelPanel.SecondLevel {
final ProvisioningTaskTO taskTO,
final String anyType,
final String anyKey,
- final String connObjectKeyValue,
+ final String fiql,
final boolean isOnSyncope,
final MultilevelPanel multiLevelPanelRef,
final PageReference pageRef) {
@@ -194,9 +195,7 @@ public class ReconTaskPanel extends MultilevelPanel.SecondLevel {
@Override
protected void onSubmit(final AjaxRequestTarget target) {
- ReconQuery reconQuery = new ReconQuery.Builder(anyType, resource).
- anyKey(anyKey).
- connObjectKeyValue(connObjectKeyValue).build();
+ ReconQuery reconQuery = new ReconQuery.Builder(anyType, resource).anyKey(anyKey).fiql(fiql).build();
try {
if (taskTO instanceof PushTaskTO) {
ReconciliationRestClient.push(reconQuery, (PushTaskTO) form.getModelObject());
diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsWizardBuilder.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsWizardBuilder.java
index 7b2771c..7f1fa02 100644
--- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsWizardBuilder.java
+++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/any/MergeLinkedAccountsWizardBuilder.java
@@ -19,8 +19,8 @@
package org.apache.syncope.client.console.wizards.any;
import com.fasterxml.jackson.databind.ObjectMapper;
-import java.util.HashMap;
import java.util.List;
+import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import javax.ws.rs.HttpMethod;
@@ -43,6 +43,7 @@ import org.apache.syncope.common.lib.request.LinkedAccountUR;
import org.apache.syncope.common.lib.request.UserUR;
import org.apache.syncope.common.lib.to.LinkedAccountTO;
import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.PatchOperation;
import org.apache.syncope.common.rest.api.Preference;
import org.apache.syncope.common.rest.api.RESTHeaders;
import org.apache.syncope.common.rest.api.batch.BatchRequestItem;
@@ -78,7 +79,7 @@ public class MergeLinkedAccountsWizardBuilder extends BaseAjaxWizardBuilder<User
@Override
protected WizardModel buildModelSteps(final UserTO modelObject, final WizardModel wizardModel) {
- this.model = new MergeLinkedAccountsWizardModel(modelObject);
+ model = new MergeLinkedAccountsWizardModel(modelObject);
wizardModel.add(new MergeLinkedAccountsSearchPanel(model, getPageReference()));
wizardModel.add(new MergeLinkedAccountsResourcesPanel(model, getPageReference()));
wizardModel.add(new MergeLinkedAccountsReviewPanel(model, getPageReference()));
@@ -91,69 +92,76 @@ public class MergeLinkedAccountsWizardBuilder extends BaseAjaxWizardBuilder<User
((AjaxWizard.NewItemCancelEvent<?>) event.getPayload()).getTarget().ifPresent(modal::close);
}
if (event.getPayload() instanceof AjaxWizard.NewItemFinishEvent) {
- Optional<AjaxRequestTarget> targetResult =
+ Optional<AjaxRequestTarget> target =
((AjaxWizard.NewItemFinishEvent<?>) event.getPayload()).getTarget();
try {
mergeAccounts();
- this.parentPanel.info(this.parentPanel.getString(Constants.OPERATION_SUCCEEDED));
- targetResult.ifPresent(target -> {
- ((BasePage) this.parentPanel.getPage()).getNotificationPanel().refresh(target);
- parentPanel.updateResultTable(target);
- modal.close(target);
+
+ parentPanel.info(parentPanel.getString(Constants.OPERATION_SUCCEEDED));
+ target.ifPresent(t -> {
+ ((BasePage) parentPanel.getPage()).getNotificationPanel().refresh(t);
+ parentPanel.updateResultTable(t);
+ modal.close(t);
});
} catch (Exception e) {
- this.parentPanel.error(this.parentPanel.getString(Constants.ERROR) + ": " + e.getMessage());
- targetResult.ifPresent(target -> ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target));
+ parentPanel.error(parentPanel.getString(Constants.ERROR) + ": " + e.getMessage());
+ target.ifPresent(t -> ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(t));
}
}
}
private void mergeAccounts() throws Exception {
- ResourceRestClient resourceRestClient = new ResourceRestClient();
-
- UserTO mergingUserTO = this.model.getMergingUser();
+ UserTO mergingUserTO = model.getMergingUser();
UserUR userUR = new UserUR();
- userUR.setKey(this.model.getBaseUser().getUsername());
+ userUR.setKey(model.getBaseUser().getUsername());
// Move linked accounts into the target/base user as linked accounts
mergingUserTO.getLinkedAccounts().forEach(acct -> {
LinkedAccountTO linkedAccount =
- new LinkedAccountTO.Builder(acct.getResource(), acct.getConnObjectKeyValue())
- .password(acct.getPassword())
- .suspended(acct.isSuspended())
- .username(acct.getUsername())
- .build();
+ new LinkedAccountTO.Builder(acct.getResource(), acct.getConnObjectKeyValue()).
+ password(acct.getPassword()).
+ suspended(acct.isSuspended()).
+ username(acct.getUsername()).
+ build();
linkedAccount.getPlainAttrs().addAll(acct.getPlainAttrs());
linkedAccount.getPrivileges().addAll(acct.getPrivileges());
- userUR.getLinkedAccounts().add(new LinkedAccountUR.Builder().linkedAccountTO(linkedAccount).build());
+ LinkedAccountUR patch = new LinkedAccountUR.Builder().
+ linkedAccountTO(linkedAccount).
+ operation(PatchOperation.ADD_REPLACE).
+ build();
+ userUR.getLinkedAccounts().add(patch);
});
// Move merging user's resources into the target/base user as a linked account
mergingUserTO.getResources().forEach(resource -> {
- String connObjectKeyValue = resourceRestClient.getConnObjectKeyValue(resource,
+ String connObjectKeyValue = ResourceRestClient.getConnObjectKeyValue(resource,
mergingUserTO.getType(), mergingUserTO.getKey());
- LinkedAccountTO linkedAccount =
- new LinkedAccountTO.Builder(resource, connObjectKeyValue)
- .build();
+ LinkedAccountTO linkedAccount = new LinkedAccountTO.Builder(resource, connObjectKeyValue).build();
linkedAccount.getPlainAttrs().addAll(mergingUserTO.getPlainAttrs());
linkedAccount.getPrivileges().addAll(mergingUserTO.getPrivileges());
- userUR.getLinkedAccounts().add(new LinkedAccountUR.Builder().linkedAccountTO(linkedAccount).build());
+ LinkedAccountUR patch = new LinkedAccountUR.Builder().
+ linkedAccountTO(linkedAccount).
+ operation(PatchOperation.ADD_REPLACE).
+ build();
+ userUR.getLinkedAccounts().add(patch);
});
// Move merging user into target/base user as a linked account
- String connObjectKeyValue = resourceRestClient.getConnObjectKeyValue(
- this.model.getResource().getKey(),
+ String connObjectKeyValue = ResourceRestClient.getConnObjectKeyValue(
+ model.getResource().getKey(),
mergingUserTO.getType(), mergingUserTO.getKey());
- LinkedAccountTO linkedAccount =
- new LinkedAccountTO.Builder(this.model.getResource().getKey(), connObjectKeyValue)
- .password(mergingUserTO.getPassword())
- .suspended(mergingUserTO.isSuspended())
- .username(mergingUserTO.getUsername())
- .build();
+ LinkedAccountTO linkedAccount = new LinkedAccountTO.Builder(model.getResource().getKey(), connObjectKeyValue).
+ password(mergingUserTO.getPassword()).
+ suspended(mergingUserTO.isSuspended()).
+ username(mergingUserTO.getUsername()).
+ build();
linkedAccount.getPlainAttrs().addAll(mergingUserTO.getPlainAttrs());
linkedAccount.getPrivileges().addAll(mergingUserTO.getPrivileges());
- userUR.getLinkedAccounts().add(new LinkedAccountUR.Builder().linkedAccountTO(linkedAccount).build());
+ LinkedAccountUR patch = new LinkedAccountUR.Builder().linkedAccountTO(linkedAccount).
+ operation(PatchOperation.ADD_REPLACE).
+ build();
+ userUR.getLinkedAccounts().add(patch);
BatchRequest batchRequest = SyncopeConsoleSession.get().batch();
@@ -168,7 +176,7 @@ public class MergeLinkedAccountsWizardBuilder extends BaseAjaxWizardBuilder<User
String updateUserPayload = MAPPER.writeValueAsString(userUR);
BatchRequestItem updateUser = new BatchRequestItem();
updateUser.setMethod(HttpMethod.PATCH);
- updateUser.setRequestURI("/users/" + this.model.getBaseUser().getUsername());
+ updateUser.setRequestURI("/users/" + model.getBaseUser().getUsername());
updateUser.setHeaders(new HashMap<>());
updateUser.getHeaders().put(RESTHeaders.PREFER, List.of(Preference.RETURN_NO_CONTENT.toString()));
updateUser.getHeaders().put(HttpHeaders.ACCEPT, List.of(MediaType.APPLICATION_JSON));
diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java
index 34dff66..99d2c9a 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleSession.java
@@ -67,29 +67,52 @@ public class SyncopeConsoleSession extends AuthenticatedWebSession implements Ba
private static final long serialVersionUID = 747562246415852166L;
- private static final Logger LOG = LoggerFactory.getLogger(SyncopeConsoleSession.class);
+ public enum Error {
+ SESSION_EXPIRED("error.session.expired", "Session expired: please login again"),
+ AUTHORIZATION("error.authorization", "Insufficient access rights when performing the requested operation"),
+ REST("error.rest", "There was an error while contacting the Core server");
- private final SyncopeClientFactoryBean clientFactory;
+ private final String key;
- private final SyncopeClient anonymousClient;
+ private final String fallback;
- private final PlatformInfo platformInfo;
+ Error(final String key, final String fallback) {
+ this.key = key;
+ this.fallback = fallback;
+ }
+
+ public String key() {
+ return key;
+ }
+
+ public String fallback() {
+ return fallback;
+ }
+ }
+
+ protected static final Logger LOG = LoggerFactory.getLogger(SyncopeConsoleSession.class);
- private final SystemInfo systemInfo;
+ protected final SyncopeClientFactoryBean clientFactory;
- private final Map<Class<?>, Object> services = Collections.synchronizedMap(new HashMap<>());
+ protected final SyncopeClient anonymousClient;
- private final ThreadPoolTaskExecutor executor;
+ protected final PlatformInfo platformInfo;
- private String domain;
+ protected final SystemInfo systemInfo;
- private SyncopeClient client;
+ protected final Map<Class<?>, Object> services = Collections.synchronizedMap(new HashMap<>());
- private UserTO selfTO;
+ protected final ThreadPoolTaskExecutor executor;
- private Map<String, Set<String>> auth;
+ protected String domain;
- private Roles roles;
+ protected SyncopeClient client;
+
+ protected UserTO selfTO;
+
+ protected Map<String, Set<String>> auth;
+
+ protected Roles roles;
public static SyncopeConsoleSession get() {
return (SyncopeConsoleSession) Session.get();
@@ -114,6 +137,18 @@ public class SyncopeConsoleSession extends AuthenticatedWebSession implements Ba
executor.initialize();
}
+ protected String message(final SyncopeClientException sce) {
+ return sce.getType().name() + ": " + sce.getElements().stream().collect(Collectors.joining(", "));
+ }
+
+ /**
+ * Extract and localize (if translation available) the actual message from the given exception; then, report it
+ * via {@link Session#error(java.io.Serializable)}.
+ *
+ * @see org.apache.syncope.client.lib.RestClientExceptionMapper
+ *
+ * @param e raised exception
+ */
@Override
public void onException(final Exception e) {
Throwable root = ExceptionUtils.getRootCause(e);
@@ -121,9 +156,10 @@ public class SyncopeConsoleSession extends AuthenticatedWebSession implements Ba
if (root instanceof SyncopeClientException) {
SyncopeClientException sce = (SyncopeClientException) root;
- if (!sce.isComposite()) {
- message = sce.getElements().stream().collect(Collectors.joining(", "));
- }
+ message = sce.isComposite()
+ ? sce.asComposite().getExceptions().stream().
+ map(c -> message(c)).collect(Collectors.joining("; "))
+ : message(sce);
} else if (root instanceof AccessControlException || root instanceof ForbiddenException) {
Error error = StringUtils.containsIgnoreCase(message, "expired")
? Error.SESSION_EXPIRED
@@ -256,7 +292,7 @@ public class SyncopeConsoleSession extends AuthenticatedWebSession implements Ba
public List<String> getSearchableRealms() {
Set<String> roots = auth.get(IdRepoEntitlement.REALM_LIST);
return roots.isEmpty()
- ? Collections.emptyList()
+ ? List.of()
: roots.stream().sorted().collect(Collectors.toList());
}
@@ -328,7 +364,7 @@ public class SyncopeConsoleSession extends AuthenticatedWebSession implements Ba
}
@SuppressWarnings("unchecked")
- private <T> T getCachedService(final Class<T> serviceClass) {
+ protected <T> T getCachedService(final Class<T> serviceClass) {
T service;
if (services.containsKey(serviceClass)) {
service = (T) services.get(serviceClass);
diff --git a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/SyncopeConsoleApplicationTest.java b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/SyncopeConsoleApplicationTest.java
index a0eccc2..d4c4bdb 100644
--- a/client/idrepo/console/src/test/java/org/apache/syncope/client/console/SyncopeConsoleApplicationTest.java
+++ b/client/idrepo/console/src/test/java/org/apache/syncope/client/console/SyncopeConsoleApplicationTest.java
@@ -110,7 +110,7 @@ public class SyncopeConsoleApplicationTest extends AbstractTest {
message = session.getFeedbackMessages().first();
assertNotNull(message);
assertTrue(message.isError());
- assertEquals("Error 1", message.getMessage());
+ assertEquals(ClientExceptionType.InvalidUser.name() + ": Error 1", message.getMessage());
session.getFeedbackMessages().clear();
sce = SyncopeClientException.build(ClientExceptionType.InvalidUser);
@@ -120,7 +120,7 @@ public class SyncopeConsoleApplicationTest extends AbstractTest {
message = session.getFeedbackMessages().first();
assertNotNull(message);
assertTrue(message.isError());
- assertEquals("Error 1, Error 2", message.getMessage());
+ assertEquals(ClientExceptionType.InvalidUser.name() + ": Error 1, Error 2", message.getMessage());
session.getFeedbackMessages().clear();
SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
@@ -130,7 +130,9 @@ public class SyncopeConsoleApplicationTest extends AbstractTest {
message = session.getFeedbackMessages().first();
assertNotNull(message);
assertTrue(message.isError());
- assertEquals(scce.getMessage(), message.getMessage());
+ assertTrue(StringUtils.contains((CharSequence) message.getMessage(),
+ ClientExceptionType.InvalidExternalResource.name()));
+ assertTrue(StringUtils.contains((CharSequence) message.getMessage(), ClientExceptionType.InvalidUser.name()));
session.getFeedbackMessages().clear();
}
}
diff --git a/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ReconQuery.java b/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ReconQuery.java
index 152993a..4aaf620 100644
--- a/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ReconQuery.java
+++ b/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/ReconQuery.java
@@ -48,8 +48,8 @@ public class ReconQuery implements Serializable {
return this;
}
- public Builder connObjectKeyValue(final String connObjectKeyValue) {
- instance.setConnObjectKeyValue(connObjectKeyValue);
+ public Builder fiql(final String fiql) {
+ instance.setFiql(fiql);
return this;
}
@@ -82,7 +82,7 @@ public class ReconQuery implements Serializable {
private String resourceKey;
- private String connObjectKeyValue;
+ private String fiql;
private Set<String> moreAttrsToGet;
@@ -115,13 +115,13 @@ public class ReconQuery implements Serializable {
this.resourceKey = resourceKey;
}
- public String getConnObjectKeyValue() {
- return connObjectKeyValue;
+ public String getFiql() {
+ return fiql;
}
- @QueryParam("connObjectKeyValue")
- public void setConnObjectKeyValue(final String connObjectKeyValue) {
- this.connObjectKeyValue = connObjectKeyValue;
+ @QueryParam(JAXRSService.PARAM_FIQL)
+ public void setFiql(final String fiql) {
+ this.fiql = fiql;
}
public Set<String> getMoreAttrsToGet() {
diff --git a/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReconciliationService.java b/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReconciliationService.java
index a2c8193..6e1e480 100644
--- a/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReconciliationService.java
+++ b/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReconciliationService.java
@@ -69,6 +69,7 @@ public interface ReconciliationService extends JAXRSService {
*
* @param query query conditions
* @param pushTask push specification
+ * @return push report
*/
@ApiResponses(
@ApiResponse(responseCode = "204", description = "Operation was successful"))
@@ -76,13 +77,14 @@ public interface ReconciliationService extends JAXRSService {
@Path("push")
@Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
- void push(@BeanParam ReconQuery query, @NotNull PushTaskTO pushTask);
+ List<ProvisioningReport> push(@BeanParam ReconQuery query, @NotNull PushTaskTO pushTask);
/**
* Pulls the matching user, group, any object or linked account from the External Resource into Syncope.
*
* @param query query conditions
* @param pullTask pull specification
+ * @return pull report
*/
@ApiResponses(
@ApiResponse(responseCode = "204", description = "Operation was successful"))
@@ -90,7 +92,7 @@ public interface ReconciliationService extends JAXRSService {
@Path("pull")
@Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
@Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
- void pull(@BeanParam ReconQuery query, @NotNull PullTaskTO pullTask);
+ List<ProvisioningReport> pull(@BeanParam ReconQuery query, @NotNull PullTaskTO pullTask);
/**
* Export a list of any objects matching the given query as CSV according to the provided specification.
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/ConnObjectTO.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/ConnObjectTO.java
index fc1158f..9b72672 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/ConnObjectTO.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/ConnObjectTO.java
@@ -33,8 +33,18 @@ public class ConnObjectTO implements BaseBean {
private static final long serialVersionUID = 5139554911265442497L;
+ private String fiql;
+
private final Set<Attr> attrs = new LinkedHashSet<>();
+ public String getFiql() {
+ return fiql;
+ }
+
+ public void setFiql(final String fiql) {
+ this.fiql = fiql;
+ }
+
@JacksonXmlElementWrapper(localName = "attrs")
@JacksonXmlProperty(localName = "attr")
public Set<Attr> getAttrs() {
@@ -49,6 +59,7 @@ public class ConnObjectTO implements BaseBean {
@Override
public int hashCode() {
return new HashCodeBuilder().
+ append(fiql).
append(attrs).
build();
}
@@ -66,6 +77,7 @@ public class ConnObjectTO implements BaseBean {
}
final ConnObjectTO other = (ConnObjectTO) obj;
return new EqualsBuilder().
+ append(fiql, other.fiql).
append(attrs, other.attrs).
build();
}
diff --git a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java
index 3ef81ac..5b4381c 100644
--- a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java
+++ b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ReconciliationLogic.java
@@ -26,6 +26,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
+import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.syncope.common.lib.Attr;
@@ -40,7 +41,6 @@ import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.common.lib.types.ClientExceptionType;
import org.apache.syncope.common.lib.types.MatchType;
import org.apache.syncope.common.rest.api.beans.CSVPullSpec;
-import org.apache.syncope.common.rest.api.beans.ReconQuery;
import org.apache.syncope.core.persistence.api.dao.AnyDAO;
import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
@@ -69,6 +69,10 @@ 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.dao.search.SearchCond;
import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.VirSchema;
+import org.apache.syncope.core.provisioning.api.pushpull.ConstantReconFilterBuilder;
+import org.apache.syncope.core.provisioning.api.pushpull.KeyValueReconFilterBuilder;
+import org.apache.syncope.core.provisioning.api.pushpull.ReconFilterBuilder;
import org.apache.syncope.core.provisioning.java.pushpull.stream.CSVStreamConnector;
import org.apache.syncope.core.provisioning.api.pushpull.SyncopeSinglePullExecutor;
import org.apache.syncope.core.provisioning.api.pushpull.SyncopeSinglePushExecutor;
@@ -82,12 +86,18 @@ import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
import org.apache.syncope.core.spring.security.AuthContextUtils;
import org.identityconnectors.framework.common.objects.Attribute;
import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.OperationOptions;
+import org.identityconnectors.framework.common.objects.SearchResult;
+import org.identityconnectors.framework.common.objects.SyncDeltaBuilder;
+import org.identityconnectors.framework.common.objects.SyncDeltaType;
+import org.identityconnectors.framework.common.objects.SyncToken;
import org.identityconnectors.framework.common.objects.Uid;
+import org.identityconnectors.framework.common.objects.filter.Filter;
+import org.identityconnectors.framework.spi.SearchResultsHandler;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component;
-import org.springframework.util.CollectionUtils;
@Component
public class ReconciliationLogic extends AbstractTransactionalLogic<EntityTO> {
@@ -168,7 +178,7 @@ public class ReconciliationLogic extends AbstractTransactionalLogic<EntityTO> {
final String connObjectKeyValue,
final Set<Attribute> attrs) {
- ConnObjectTO connObjectTO = ConnObjectUtils.getConnObjectTO(attrs);
+ ConnObjectTO connObjectTO = ConnObjectUtils.getConnObjectTO(null, attrs);
connObjectTO.getAttrs().add(new Attr.Builder(connObjectKeyItem.getExtAttrName()).
value(connObjectKeyValue).build());
connObjectTO.getAttrs().add(new Attr.Builder(Uid.NAME).
@@ -209,71 +219,111 @@ public class ReconciliationLogic extends AbstractTransactionalLogic<EntityTO> {
}
@PreAuthorize("hasRole('" + IdMEntitlement.RESOURCE_GET_CONNOBJECT + "')")
- public ReconStatus status(final ReconQuery query) {
- Provision provision = getProvision(query.getAnyTypeKey(), query.getResourceKey());
+ public ReconStatus status(final String anyTypeKey, final String resourceKey, final String anyKey) {
+ Provision provision = getProvision(anyTypeKey, resourceKey);
MappingItem connObjectKeyItem = MappingUtils.getConnObjectKeyItem(provision).
orElseThrow(() -> new NotFoundException(
"ConnObjectKey for " + provision.getAnyType().getKey()
+ " on resource '" + provision.getResource().getKey() + "'"));
+ Any<?> any = getAny(provision, anyKey);
+
ReconStatus status = new ReconStatus();
+ status.setMatchType(MatchType.ANY);
+ status.setAnyTypeKind(any.getType().getKind());
+ status.setAnyKey(any.getKey());
+ status.setRealm(any.getRealm().getFullPath());
+ status.setOnSyncope(getOnSyncope(any, connObjectKeyItem, provision));
+
+ List<ConnectorObject> connObjs = outboundMatcher.match(
+ connFactory.getConnector(provision.getResource()), any, provision, Optional.empty());
+ if (!connObjs.isEmpty()) {
+ status.setOnResource(ConnObjectUtils.getConnObjectTO(
+ outboundMatcher.getFIQL(connObjs.get(0), provision), connObjs.get(0).getAttributes()));
+
+ if (connObjs.size() > 1) {
+ LOG.warn("Expected single match, found {}", connObjs);
+ } else {
+ virAttrHandler.setValues(any, connObjs.get(0));
+ }
+ }
- if (query.getConnObjectKeyValue() != null) {
- inboundMatcher.matchByConnObjectKeyValue(connObjectKeyItem, query.getConnObjectKeyValue(), provision).
- stream().findFirst().ifPresent(match -> {
- if (match.getAny() != null) {
- status.setMatchType(MatchType.ANY);
- status.setAnyTypeKind(match.getAny().getType().getKind());
- status.setAnyKey(match.getAny().getKey());
- status.setRealm(match.getAny().getRealm().getFullPath());
- status.setOnSyncope(getOnSyncope(match.getAny(), connObjectKeyItem, provision));
- } else if (match.getLinkedAccount() != null) {
- status.setMatchType(MatchType.LINKED_ACCOUNT);
- status.setAnyTypeKind(AnyTypeKind.USER);
- status.setAnyKey(match.getLinkedAccount().getOwner().getKey());
- status.setRealm(match.getLinkedAccount().getOwner().getRealm().getFullPath());
- status.setOnSyncope(getOnSyncope(match.getLinkedAccount(), connObjectKeyItem, provision));
- }
- });
+ return status;
+ }
- Optional<String[]> moreAttrsToGet = CollectionUtils.isEmpty(query.getMoreAttrsToGet())
- ? Optional.empty()
- : Optional.of(query.getMoreAttrsToGet().toArray(new String[0]));
+ private SyncDeltaBuilder syncDeltaBuilder(
+ final Provision provision,
+ final Filter filter,
+ final Set<String> moreAttrsToGet) {
+
+ Stream<MappingItem> mapItems = Stream.concat(
+ provision.getMapping().getItems().stream(),
+ virSchemaDAO.findByProvision(provision).stream().map(VirSchema::asLinkingMappingItem));
+ OperationOptions options = MappingUtils.buildOperationOptions(mapItems, moreAttrsToGet.toArray(new String[0]));
+
+ SyncDeltaBuilder syncDeltaBuilder = new SyncDeltaBuilder().
+ setToken(new SyncToken("")).
+ setDeltaType(SyncDeltaType.CREATE_OR_UPDATE).
+ setObjectClass(provision.getObjectClass());
+ connFactory.getConnector(provision.getResource()).
+ search(provision.getObjectClass(), filter, new SearchResultsHandler() {
+
+ @Override
+ public boolean handle(final ConnectorObject connObj) {
+ syncDeltaBuilder.setObject(connObj);
+ return false;
+ }
+
+ @Override
+ public void handleResult(final SearchResult sr) {
+ // do nothing
+ }
+ }, 1, null, List.of(), options);
+
+ return syncDeltaBuilder;
+ }
- outboundMatcher.matchByConnObjectKeyValue(
- connFactory.getConnector(provision.getResource()),
- connObjectKeyItem,
- query.getConnObjectKeyValue(),
- provision,
- moreAttrsToGet,
- Optional.empty()).
- ifPresent(connObj -> {
- status.setOnResource(ConnObjectUtils.getConnObjectTO(connObj.getAttributes()));
+ @PreAuthorize("hasRole('" + IdMEntitlement.RESOURCE_GET_CONNOBJECT + "')")
+ public ReconStatus status(
+ final String anyTypeKey,
+ final String resourceKey,
+ final Filter filter,
+ final Set<String> moreAttrsToGet) {
- if (status.getMatchType() == MatchType.ANY && StringUtils.isNotBlank(status.getAnyKey())) {
- virAttrHandler.setValues(getAny(provision, status.getAnyKey()), connObj);
- }
- });
- }
- if (query.getAnyKey() != null) {
- Any<?> any = getAny(provision, query.getAnyKey());
- status.setMatchType(MatchType.ANY);
- status.setAnyTypeKind(any.getType().getKind());
- status.setAnyKey(any.getKey());
- status.setRealm(any.getRealm().getFullPath());
- status.setOnSyncope(getOnSyncope(any, connObjectKeyItem, provision));
-
- List<ConnectorObject> connObjs = outboundMatcher.match(
- connFactory.getConnector(provision.getResource()), any, provision, Optional.empty());
- if (!connObjs.isEmpty()) {
- status.setOnResource(ConnObjectUtils.getConnObjectTO(connObjs.get(0).getAttributes()));
-
- if (connObjs.size() > 1) {
- LOG.warn("Expected single match, found {}", connObjs);
- } else {
- virAttrHandler.setValues(any, connObjs.get(0));
+ Provision provision = getProvision(anyTypeKey, resourceKey);
+
+ SyncDeltaBuilder syncDeltaBuilder = syncDeltaBuilder(provision, filter, moreAttrsToGet);
+
+ ReconStatus status = new ReconStatus();
+ if (syncDeltaBuilder.getObject() != null) {
+ MappingItem connObjectKeyItem = MappingUtils.getConnObjectKeyItem(provision).
+ orElseThrow(() -> new NotFoundException(
+ "ConnObjectKey for " + provision.getAnyType().getKey()
+ + " on resource '" + provision.getResource().getKey() + "'"));
+
+ inboundMatcher.match(syncDeltaBuilder.build(), provision).stream().findFirst().ifPresent(match -> {
+ if (match.getAny() != null) {
+ status.setMatchType(MatchType.ANY);
+ status.setAnyTypeKind(match.getAny().getType().getKind());
+ status.setAnyKey(match.getAny().getKey());
+ status.setRealm(match.getAny().getRealm().getFullPath());
+ status.setOnSyncope(getOnSyncope(match.getAny(), connObjectKeyItem, provision));
+ } else if (match.getLinkedAccount() != null) {
+ status.setMatchType(MatchType.LINKED_ACCOUNT);
+ status.setAnyTypeKind(AnyTypeKind.USER);
+ status.setAnyKey(match.getLinkedAccount().getOwner().getKey());
+ status.setRealm(match.getLinkedAccount().getOwner().getRealm().getFullPath());
+ status.setOnSyncope(getOnSyncope(match.getLinkedAccount(), connObjectKeyItem, provision));
}
+ });
+
+ status.setOnResource(ConnObjectUtils.getConnObjectTO(
+ outboundMatcher.getFIQL(syncDeltaBuilder.getObject(), provision),
+ syncDeltaBuilder.getObject().getAttributes()));
+
+ if (status.getMatchType() == MatchType.ANY && StringUtils.isNotBlank(status.getAnyKey())) {
+ virAttrHandler.setValues(getAny(provision, status.getAnyKey()), syncDeltaBuilder.getObject());
}
}
@@ -281,101 +331,105 @@ public class ReconciliationLogic extends AbstractTransactionalLogic<EntityTO> {
}
@PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_EXECUTE + "')")
- public void push(final ReconQuery query, final PushTaskTO pushTask) {
- Provision provision = getProvision(query.getAnyTypeKey(), query.getResourceKey());
+ public List<ProvisioningReport> push(
+ final String anyTypeKey,
+ final String resourceKey,
+ final String anyKey,
+ final PushTaskTO pushTask) {
- MappingItem connObjectKeyItem = MappingUtils.getConnObjectKeyItem(provision).
- orElseThrow(() -> new NotFoundException(
- "ConnObjectKey for " + provision.getAnyType().getKey()
- + " on resource '" + provision.getResource().getKey() + "'"));
+ Provision provision = getProvision(anyTypeKey, resourceKey);
SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Reconciliation);
List<ProvisioningReport> results = new ArrayList<>();
-
- if (query.getConnObjectKeyValue() != null) {
- inboundMatcher.matchByConnObjectKeyValue(connObjectKeyItem, query.getConnObjectKeyValue(), provision).
- stream().findFirst().ifPresent(match -> {
- try {
- if (match.getMatchTarget() == MatchType.ANY) {
- results.addAll(singlePushExecutor.push(
- provision,
- connFactory.getConnector(provision.getResource()),
- match.getAny(),
- pushTask));
- if (!results.isEmpty()
- && results.get(0).getStatus() == ProvisioningReport.Status.FAILURE) {
-
- sce.getElements().add(results.get(0).getMessage());
- }
- } else {
- ProvisioningReport result = singlePushExecutor.push(
- provision,
- connFactory.getConnector(provision.getResource()),
- match.getLinkedAccount(),
- pushTask);
- if (result.getStatus() == ProvisioningReport.Status.FAILURE) {
- sce.getElements().add(result.getMessage());
- } else {
- results.add(result);
- }
- }
- } catch (JobExecutionException e) {
- sce.getElements().add(e.getMessage());
- }
- });
- }
-
- if (sce.isEmpty() && results.isEmpty() && query.getAnyKey() != null) {
- try {
- results.addAll(singlePushExecutor.push(
- provision,
- connFactory.getConnector(provision.getResource()),
- getAny(provision, query.getAnyKey()),
- pushTask));
- if (!results.isEmpty() && results.get(0).getStatus() == ProvisioningReport.Status.FAILURE) {
- sce.getElements().add(results.get(0).getMessage());
- }
- } catch (JobExecutionException e) {
- sce.getElements().add(e.getMessage());
+ try {
+ results.addAll(singlePushExecutor.push(
+ provision,
+ connFactory.getConnector(provision.getResource()),
+ getAny(provision, anyKey),
+ pushTask));
+ if (!results.isEmpty() && results.get(0).getStatus() == ProvisioningReport.Status.FAILURE) {
+ sce.getElements().add(results.get(0).getMessage());
}
+ } catch (JobExecutionException e) {
+ sce.getElements().add(e.getMessage());
}
if (!sce.isEmpty()) {
throw sce;
}
+
+ return results;
}
@PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_EXECUTE + "')")
- public void pull(final ReconQuery query, final PullTaskTO pullTask) {
- Provision provision = getProvision(query.getAnyTypeKey(), query.getResourceKey());
+ public List<ProvisioningReport> push(
+ final String anyTypeKey,
+ final String resourceKey,
+ final Filter filter,
+ final Set<String> moreAttrsToGet,
+ final PushTaskTO pushTask) {
+
+ Provision provision = getProvision(anyTypeKey, resourceKey);
- Optional<String> connObjectKeyValue = Optional.ofNullable(query.getConnObjectKeyValue());
- if (query.getAnyKey() != null) {
- Any<?> any = getAny(provision, query.getAnyKey());
- connObjectKeyValue = mappingManager.getConnObjectKeyValue(any, provision);
+ SyncDeltaBuilder syncDeltaBuilder = syncDeltaBuilder(provision, filter, moreAttrsToGet);
+
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Reconciliation);
+ List<ProvisioningReport> results = new ArrayList<>();
+
+ if (syncDeltaBuilder.getObject() != null) {
+ inboundMatcher.match(syncDeltaBuilder.build(), provision).stream().findFirst().ifPresent(match -> {
+ try {
+ if (match.getMatchTarget() == MatchType.ANY) {
+ results.addAll(singlePushExecutor.push(
+ provision,
+ connFactory.getConnector(provision.getResource()),
+ match.getAny(),
+ pushTask));
+ if (!results.isEmpty() && results.get(0).getStatus() == ProvisioningReport.Status.FAILURE) {
+ sce.getElements().add(results.get(0).getMessage());
+ }
+ } else {
+ ProvisioningReport result = singlePushExecutor.push(
+ provision,
+ connFactory.getConnector(provision.getResource()),
+ match.getLinkedAccount(),
+ pushTask);
+ if (result.getStatus() == ProvisioningReport.Status.FAILURE) {
+ sce.getElements().add(result.getMessage());
+ } else {
+ results.add(result);
+ }
+ }
+ } catch (JobExecutionException e) {
+ sce.getElements().add(e.getMessage());
+ }
+ });
}
- if (connObjectKeyValue.isEmpty()) {
- throw new NotFoundException(
- "ConnObjectKey for " + provision.getAnyType().getKey()
- + " on resource '" + provision.getResource().getKey() + "'");
+
+ if (!sce.isEmpty()) {
+ throw sce;
}
+ return results;
+ }
+
+ private List<ProvisioningReport> pull(
+ final Provision provision,
+ final ReconFilterBuilder reconFilterBuilder,
+ final PullTaskTO pullTask) {
+
if (pullTask.getDestinationRealm() == null || realmDAO.findByFullPath(pullTask.getDestinationRealm()) == null) {
throw new NotFoundException("Realm " + pullTask.getDestinationRealm());
}
- if (provision.getMapping().getConnObjectKeyItem().isEmpty()) {
- throw new NotFoundException(
- "ConnObjectKey cannot be determined for mapping " + provision.getMapping().getKey());
- }
SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Reconciliation);
+ List<ProvisioningReport> results = new ArrayList<>();
try {
- List<ProvisioningReport> results = singlePullExecutor.pull(
+ results.addAll(singlePullExecutor.pull(
provision,
connFactory.getConnector(provision.getResource()),
- provision.getMapping().getConnObjectKeyItem().get().getExtAttrName(),
- connObjectKeyValue.get(),
- pullTask);
+ reconFilterBuilder,
+ pullTask));
if (!results.isEmpty() && results.get(0).getStatus() == ProvisioningReport.Status.FAILURE) {
sce.getElements().add(results.get(0).getMessage());
}
@@ -386,6 +440,52 @@ public class ReconciliationLogic extends AbstractTransactionalLogic<EntityTO> {
if (!sce.isEmpty()) {
throw sce;
}
+
+ return results;
+ }
+
+ @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_EXECUTE + "')")
+ public List<ProvisioningReport> pull(
+ final String anyTypeKey,
+ final String resourceKey,
+ final String anyKey,
+ final PullTaskTO pullTask) {
+
+ Provision provision = getProvision(anyTypeKey, resourceKey);
+
+ Any<?> any = getAny(provision, anyKey);
+
+ if (!provision.getMapping().getConnObjectKeyItem().isPresent()) {
+ throw new NotFoundException(
+ "ConnObjectKey cannot be determined for mapping " + provision.getMapping().getKey());
+ }
+
+ String connObjectKeyValue = mappingManager.getConnObjectKeyValue(any, provision).
+ orElseThrow(() -> new NotFoundException(
+ "ConnObjectKey for " + provision.getAnyType().getKey()
+ + " on resource '" + provision.getResource().getKey() + "'"));
+
+ return pull(
+ provision,
+ new KeyValueReconFilterBuilder(
+ provision.getMapping().getConnObjectKeyItem().get().getExtAttrName(), connObjectKeyValue),
+ pullTask);
+ }
+
+ @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_EXECUTE + "')")
+ public List<ProvisioningReport> pull(
+ final String anyTypeKey,
+ final String resourceKey,
+ final Filter filter,
+ final Set<String> moreAttrsToGet,
+ final PullTaskTO pullTask) {
+
+ Provision provision = getProvision(anyTypeKey, resourceKey);
+
+ return pull(
+ provision,
+ new ConstantReconFilterBuilder(filter),
+ pullTask);
}
private CsvSchema.Builder csvSchema(final AbstractCSVSpec spec) {
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 1eb41fd..9a0ca55 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
@@ -336,7 +336,8 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
virAttrHandler.setValues(any, connObjs.get(0));
}
- return ConnObjectUtils.getConnObjectTO(connObjs.get(0).getAttributes());
+ return ConnObjectUtils.getConnObjectTO(
+ outboundMatcher.getFIQL(connObjs.get(0), provision), connObjs.get(0).getAttributes());
}
@PreAuthorize("hasRole('" + IdMEntitlement.RESOURCE_GET_CONNOBJECT + "')")
@@ -353,20 +354,18 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
"ConnObjectKey mapping for " + provision.getAnyType().getKey()
+ " on resource '" + provision.getResource().getKey() + "'"));
- Optional<ConnectorObject> connObj = outboundMatcher.matchByConnObjectKeyValue(
+ return outboundMatcher.matchByConnObjectKeyValue(
connFactory.getConnector(provision.getResource()),
connObjectKeyItem,
connObjectKeyValue,
provision,
Optional.empty(),
- Optional.empty());
- if (connObj.isPresent()) {
- return ConnObjectUtils.getConnObjectTO(connObj.get().getAttributes());
- }
-
- throw new NotFoundException(
+ Optional.empty()).
+ map(connectorObject -> ConnObjectUtils.getConnObjectTO(
+ outboundMatcher.getFIQL(connectorObject, provision), connectorObject.getAttributes())).
+ orElseThrow(() -> new NotFoundException(
"Object " + connObjectKeyValue + " with class " + provision.getObjectClass()
- + " not found on resource " + provision.getResource().getKey());
+ + " not found on resource " + provision.getResource().getKey()));
}
@PreAuthorize("hasRole('" + IdMEntitlement.RESOURCE_LIST_CONNOBJECT + "')")
@@ -381,6 +380,7 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
final List<OrderByClause> orderBy) {
ExternalResource resource;
+ Provision provision;
ObjectClass objectClass;
OperationOptions options;
if (SyncopeConstants.REALM_ANYTYPE.equals(anyTypeKey)) {
@@ -392,11 +392,12 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
throw new NotFoundException("Realm provisioning for resource '" + key + '\'');
}
+ provision = null;
objectClass = resource.getOrgUnit().getObjectClass();
options = MappingUtils.buildOperationOptions(
resource.getOrgUnit().getItems().stream(), moreAttrsToGet.toArray(new String[0]));
} else {
- Provision provision = getProvision(key, anyTypeKey);
+ provision = getProvision(key, anyTypeKey);
resource = provision.getResource();
objectClass = provision.getObjectClass();
@@ -414,7 +415,9 @@ public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
@Override
public boolean handle(final ConnectorObject connectorObject) {
- connObjects.add(ConnObjectUtils.getConnObjectTO(connectorObject.getAttributes()));
+ connObjects.add(ConnObjectUtils.getConnObjectTO(
+ provision == null ? null : outboundMatcher.getFIQL(connectorObject, provision),
+ connectorObject.getAttributes()));
// safety protection against uncontrolled result size
count++;
return count < size;
diff --git a/core/idm/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReconciliationServiceImpl.java b/core/idm/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReconciliationServiceImpl.java
index 6f3b1a1..881544b 100644
--- a/core/idm/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReconciliationServiceImpl.java
+++ b/core/idm/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReconciliationServiceImpl.java
@@ -19,17 +19,25 @@
package org.apache.syncope.core.rest.cxf.service;
import java.io.InputStream;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import javax.validation.ValidationException;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
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.ProvisioningReport;
import org.apache.syncope.common.lib.to.PullTaskTO;
import org.apache.syncope.common.lib.to.PushTaskTO;
import org.apache.syncope.common.lib.to.ReconStatus;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
import org.apache.syncope.common.rest.api.RESTHeaders;
import org.apache.syncope.common.rest.api.beans.AnyQuery;
import org.apache.syncope.common.rest.api.beans.CSVPullSpec;
@@ -38,7 +46,9 @@ import org.apache.syncope.common.rest.api.beans.ReconQuery;
import org.apache.syncope.common.rest.api.service.ReconciliationService;
import org.apache.syncope.core.logic.ReconciliationLogic;
import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.api.search.FilterVisitor;
import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.identityconnectors.framework.common.objects.filter.Filter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -49,44 +59,91 @@ public class ReconciliationServiceImpl extends AbstractServiceImpl implements Re
private ReconciliationLogic logic;
private void validate(final ReconQuery reconQuery) {
- if ((reconQuery.getAnyKey() == null && reconQuery.getConnObjectKeyValue() == null)
- || (reconQuery.getAnyKey() != null && reconQuery.getConnObjectKeyValue() != null)) {
+ if ((reconQuery.getAnyKey() == null && reconQuery.getFiql() == null)
+ || (reconQuery.getAnyKey() != null && reconQuery.getFiql() != null)) {
- throw new ValidationException("Either provide anyKey or connObjectKeyValue, not both");
+ throw new ValidationException("Either provide anyKey or fiql, not both");
}
}
+ private Pair<Filter, Set<String>> buildFromFIQL(final ReconQuery reconQuery) {
+ Filter filter = null;
+ Set<String> moreAttrsToGet = new HashSet<>();
+ if (reconQuery.getMoreAttrsToGet() != null) {
+ moreAttrsToGet.addAll(reconQuery.getMoreAttrsToGet());
+ }
+ if (StringUtils.isNotBlank(reconQuery.getFiql())) {
+ try {
+ FilterVisitor visitor = new FilterVisitor();
+ SearchCondition<SearchBean> sc = searchContext.getCondition(reconQuery.getFiql(), SearchBean.class);
+ sc.accept(visitor);
+
+ filter = visitor.getQuery();
+ moreAttrsToGet.addAll(visitor.getAttrs());
+ } catch (Exception e) {
+ LOG.error("Invalid FIQL expression: {}", reconQuery.getFiql(), e);
+
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSearchExpression);
+ sce.getElements().add(reconQuery.getFiql());
+ sce.getElements().add(ExceptionUtils.getRootCauseMessage(e));
+ throw sce;
+ }
+ }
+
+ return Pair.of(filter, moreAttrsToGet);
+ }
+
@Override
- public ReconStatus status(final ReconQuery reconQuery) {
- validate(reconQuery);
- return logic.status(reconQuery);
+ public ReconStatus status(final ReconQuery query) {
+ validate(query);
+
+ if (query.getAnyKey() != null) {
+ return logic.status(query.getAnyTypeKey(), query.getResourceKey(), query.getAnyKey());
+ }
+
+ Pair<Filter, Set<String>> fromFIQL = buildFromFIQL(query);
+ return logic.status(query.getAnyTypeKey(), query.getResourceKey(), fromFIQL.getLeft(), fromFIQL.getRight());
}
@Override
- public void push(final ReconQuery reconQuery, final PushTaskTO pushTask) {
- validate(reconQuery);
- logic.push(reconQuery, pushTask);
+ public List<ProvisioningReport> push(final ReconQuery query, final PushTaskTO pushTask) {
+ validate(query);
+
+ if (query.getAnyKey() != null) {
+ return logic.push(query.getAnyTypeKey(), query.getResourceKey(), query.getAnyKey(), pushTask);
+ }
+
+ Pair<Filter, Set<String>> fromFIQL = buildFromFIQL(query);
+ return logic.push(
+ query.getAnyTypeKey(), query.getResourceKey(), fromFIQL.getLeft(), fromFIQL.getRight(), pushTask);
}
@Override
- public void pull(final ReconQuery reconQuery, final PullTaskTO pullTask) {
- validate(reconQuery);
- logic.pull(reconQuery, pullTask);
+ public List<ProvisioningReport> pull(final ReconQuery query, final PullTaskTO pullTask) {
+ validate(query);
+
+ if (query.getAnyKey() != null) {
+ return logic.pull(query.getAnyTypeKey(), query.getResourceKey(), query.getAnyKey(), pullTask);
+ }
+
+ Pair<Filter, Set<String>> fromFIQL = buildFromFIQL(query);
+ return logic.pull(
+ query.getAnyTypeKey(), query.getResourceKey(), fromFIQL.getLeft(), fromFIQL.getRight(), pullTask);
}
@Override
- public Response push(final AnyQuery anyQuery, final CSVPushSpec spec) {
- String realm = StringUtils.prependIfMissing(anyQuery.getRealm(), SyncopeConstants.ROOT_REALM);
+ public Response push(final AnyQuery query, final CSVPushSpec spec) {
+ String realm = StringUtils.prependIfMissing(query.getRealm(), SyncopeConstants.ROOT_REALM);
- SearchCond searchCond = StringUtils.isBlank(anyQuery.getFiql())
+ SearchCond searchCond = StringUtils.isBlank(query.getFiql())
? null
- : getSearchCond(anyQuery.getFiql(), realm);
+ : getSearchCond(query.getFiql(), realm);
- StreamingOutput sout = (os) -> logic.push(
+ StreamingOutput sout = os -> logic.push(
searchCond,
- anyQuery.getPage(),
- anyQuery.getSize(),
- getOrderByClauses(anyQuery.getOrderBy()),
+ query.getPage(),
+ query.getSize(),
+ getOrderByClauses(query.getOrderBy()),
realm,
spec,
os);
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PushCorrelationRule.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PushCorrelationRule.java
index 050cdce..256b799 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PushCorrelationRule.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PushCorrelationRule.java
@@ -18,9 +18,12 @@
*/
package org.apache.syncope.core.persistence.api.dao;
+import java.util.function.BiFunction;
import org.apache.syncope.common.lib.policy.PushCorrelationRuleConf;
import org.apache.syncope.core.persistence.api.entity.Any;
import org.apache.syncope.core.persistence.api.entity.resource.Provision;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.Uid;
import org.identityconnectors.framework.common.objects.filter.Filter;
/**
@@ -29,15 +32,34 @@ import org.identityconnectors.framework.common.objects.filter.Filter;
@FunctionalInterface
public interface PushCorrelationRule {
+ /**
+ * Default FIQL builder using __UID__.
+ */
+ BiFunction<ConnectorObject, Provision, String> DEFAULT_FIQL_BUILDER =
+ (connectorObject, provision) -> Uid.NAME + "==" + connectorObject.getUid().getUidValue();
+
default void setConf(PushCorrelationRuleConf conf) {
}
/**
- * Return a search condition.
+ * Returns a filter to match the given any with a connector object on the external resource identified by
+ * the given provision.
*
* @param any user, group or any object
* @param provision resource provision
- * @return search condition.
+ * @return filter.
*/
Filter getFilter(Any<?> any, Provision provision);
+
+ /**
+ * Returns a FIQL string to match the given connector object when searching into the external resource identified by
+ * the given provision.
+ *
+ * @param connectorObject connector object
+ * @param provision resource provision
+ * @return fiql
+ */
+ default String getFiql(ConnectorObject connectorObject, Provision provision) {
+ return DEFAULT_FIQL_BUILDER.apply(connectorObject, provision);
+ }
}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPushCorrelationRule.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPushCorrelationRule.java
index 03d1e3d..19ac31c 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPushCorrelationRule.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPushCorrelationRule.java
@@ -19,10 +19,15 @@
package org.apache.syncope.core.persistence.jpa.dao;
import java.util.ArrayList;
+import java.util.Base64;
import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
import org.apache.commons.lang3.tuple.Pair;
+import org.apache.cxf.jaxrs.ext.search.client.CompleteCondition;
import org.apache.syncope.common.lib.policy.DefaultPushCorrelationRuleConf;
import org.apache.syncope.common.lib.policy.PushCorrelationRuleConf;
+import org.apache.syncope.common.lib.search.ConnObjectTOFiqlSearchConditionBuilder;
import org.apache.syncope.common.lib.types.MappingPurpose;
import org.apache.syncope.core.persistence.api.entity.resource.Provision;
import org.identityconnectors.framework.common.objects.Attribute;
@@ -32,18 +37,26 @@ import org.apache.syncope.core.persistence.api.entity.Any;
import org.apache.syncope.core.provisioning.api.AccountGetter;
import org.apache.syncope.core.provisioning.api.MappingManager;
import org.apache.syncope.core.provisioning.api.PlainAttrGetter;
+import org.identityconnectors.common.security.GuardedByteArray;
+import org.identityconnectors.common.security.GuardedString;
+import org.identityconnectors.common.security.SecurityUtil;
import org.identityconnectors.framework.common.objects.AttributeBuilder;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
import org.identityconnectors.framework.common.objects.filter.Filter;
import org.identityconnectors.framework.common.objects.filter.FilterBuilder;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.CollectionUtils;
@PushCorrelationRuleConfClass(DefaultPushCorrelationRuleConf.class)
public class DefaultPushCorrelationRule implements PushCorrelationRule {
+ protected static final ConnObjectTOFiqlSearchConditionBuilder FIQL_BUILDER =
+ new ConnObjectTOFiqlSearchConditionBuilder();
+
@Autowired
- private MappingManager mappingManager;
+ protected MappingManager mappingManager;
- private DefaultPushCorrelationRuleConf conf;
+ protected DefaultPushCorrelationRuleConf conf;
@Override
public void setConf(final PushCorrelationRuleConf conf) {
@@ -60,8 +73,7 @@ public class DefaultPushCorrelationRule implements PushCorrelationRule {
List<Filter> filters = new ArrayList<>();
provision.getMapping().getItems().stream().filter(
- item -> conf.getSchemas().contains(item.getIntAttrName())
- && (item.getPurpose() == MappingPurpose.PROPAGATION || item.getPurpose() == MappingPurpose.BOTH)).
+ item -> conf.getSchemas().contains(item.getIntAttrName()) && item.getPurpose() != MappingPurpose.NONE).
forEach(item -> {
Pair<String, Attribute> attr = mappingManager.prepareAttr(
provision,
@@ -90,4 +102,45 @@ public class DefaultPushCorrelationRule implements PushCorrelationRule {
? FilterBuilder.or(filters)
: FilterBuilder.and(filters);
}
+
+ @Override
+ public String getFiql(final ConnectorObject connectorObject, final Provision provision) {
+ List<CompleteCondition> conditions = new ArrayList<>();
+
+ provision.getMapping().getItems().stream().filter(
+ item -> conf.getSchemas().contains(item.getIntAttrName()) && item.getPurpose() != MappingPurpose.NONE).
+ forEach(item -> Optional.ofNullable(connectorObject.getAttributeByName(item.getExtAttrName())).
+ ifPresent(attr -> {
+ if (CollectionUtils.isEmpty(attr.getValue())) {
+ conditions.add(FIQL_BUILDER.isNull(attr.getName()));
+ } else {
+ List<CompleteCondition> valueConditions = new ArrayList<>();
+
+ attr.getValue().stream().filter(Objects::nonNull).forEach(value -> {
+ if (value instanceof GuardedString) {
+ valueConditions.add(FIQL_BUILDER.is(attr.getName()).
+ equalTo(SecurityUtil.decrypt((GuardedString) value)));
+ } else if (value instanceof GuardedByteArray) {
+ valueConditions.add(FIQL_BUILDER.is(attr.getName()).
+ equalTo(new String(SecurityUtil.decrypt((GuardedByteArray) value))));
+ } else if (value instanceof byte[]) {
+ valueConditions.add(FIQL_BUILDER.is(attr.getName()).
+ equalTo(Base64.getEncoder().encodeToString((byte[]) value)));
+ } else {
+ valueConditions.add(FIQL_BUILDER.is(attr.getName()).equalTo(value.toString()));
+ }
+ });
+
+ if (!valueConditions.isEmpty()) {
+ conditions.add(valueConditions.size() == 1
+ ? valueConditions.get(0)
+ : FIQL_BUILDER.and(valueConditions));
+ }
+ }
+ }));
+
+ return conf.isOrSchemas()
+ ? FIQL_BUILDER.or(conditions).query()
+ : FIQL_BUILDER.and(conditions).query();
+ }
}
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationReporter.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationReporter.java
index 814f75b..fd7ccbe 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationReporter.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationReporter.java
@@ -44,6 +44,7 @@ public interface PropagationReporter {
* @param taskInfo propagation task
* @param execStatus propagation execution status
* @param failureReason propagation execution failure message
+ * @param fiql FIQL string to match the connector objects into the external resource
* @param beforeObj retrieved connector object before operation execution
* @param afterObj retrieved connector object after operation execution
*/
@@ -51,6 +52,7 @@ public interface PropagationReporter {
PropagationTaskInfo taskInfo,
ExecStatus execStatus,
String failureReason,
+ String fiql,
ConnectorObject beforeObj,
ConnectorObject afterObj);
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePullExecutor.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/ConstantReconFilterBuilder.java
similarity index 58%
copy from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePullExecutor.java
copy to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/ConstantReconFilterBuilder.java
index 7614291..d980602 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePullExecutor.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/ConstantReconFilterBuilder.java
@@ -18,20 +18,18 @@
*/
package org.apache.syncope.core.provisioning.api.pushpull;
-import org.apache.syncope.common.lib.to.ProvisioningReport;
-import java.util.List;
-import org.apache.syncope.common.lib.to.PullTaskTO;
-import org.apache.syncope.core.persistence.api.entity.resource.Provision;
-import org.apache.syncope.core.provisioning.api.Connector;
-import org.quartz.JobExecutionException;
+import org.identityconnectors.framework.common.objects.filter.Filter;
-@FunctionalInterface
-public interface SyncopeSinglePullExecutor {
+public class ConstantReconFilterBuilder implements ReconFilterBuilder {
- List<ProvisioningReport> pull(
- Provision provision,
- Connector connector,
- String connObjectKey,
- String connObjectValue,
- PullTaskTO pullTaskTO) throws JobExecutionException;
+ private final Filter filter;
+
+ public ConstantReconFilterBuilder(final Filter filter) {
+ this.filter = filter;
+ }
+
+ @Override
+ public Filter build() {
+ return filter;
+ }
}
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePullExecutor.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/KeyValueReconFilterBuilder.java
similarity index 58%
copy from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePullExecutor.java
copy to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/KeyValueReconFilterBuilder.java
index 7614291..1b5d349 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePullExecutor.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/KeyValueReconFilterBuilder.java
@@ -18,20 +18,23 @@
*/
package org.apache.syncope.core.provisioning.api.pushpull;
-import org.apache.syncope.common.lib.to.ProvisioningReport;
-import java.util.List;
-import org.apache.syncope.common.lib.to.PullTaskTO;
-import org.apache.syncope.core.persistence.api.entity.resource.Provision;
-import org.apache.syncope.core.provisioning.api.Connector;
-import org.quartz.JobExecutionException;
+import org.identityconnectors.framework.common.objects.AttributeBuilder;
+import org.identityconnectors.framework.common.objects.filter.Filter;
+import org.identityconnectors.framework.common.objects.filter.FilterBuilder;
-@FunctionalInterface
-public interface SyncopeSinglePullExecutor {
+public class KeyValueReconFilterBuilder implements ReconFilterBuilder {
- List<ProvisioningReport> pull(
- Provision provision,
- Connector connector,
- String connObjectKey,
- String connObjectValue,
- PullTaskTO pullTaskTO) throws JobExecutionException;
+ private final String key;
+
+ private final String value;
+
+ public KeyValueReconFilterBuilder(final String key, final String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ @Override
+ public Filter build() {
+ return FilterBuilder.equalTo(AttributeBuilder.build(key, value));
+ }
}
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePullExecutor.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePullExecutor.java
index 7614291..4ace3c8 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePullExecutor.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePullExecutor.java
@@ -31,7 +31,6 @@ public interface SyncopeSinglePullExecutor {
List<ProvisioningReport> pull(
Provision provision,
Connector connector,
- String connObjectKey,
- String connObjectValue,
+ ReconFilterBuilder reconFilterBuilder,
PullTaskTO pullTaskTO) throws JobExecutionException;
}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
index ed17e68..29a4f26 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
@@ -18,6 +18,15 @@
*/
package org.apache.syncope.core.provisioning.java.data;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.syncope.common.lib.Attr;
@@ -74,24 +83,18 @@ import org.apache.syncope.core.provisioning.api.VirAttrHandler;
import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
import org.apache.syncope.core.provisioning.api.PropagationByResource;
import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.java.pushpull.OutboundMatcher;
import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.AttributeBuilder;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder;
import org.identityconnectors.framework.common.objects.Uid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
-
abstract class AbstractAnyDataBinder {
protected static final Logger LOG = LoggerFactory.getLogger(AbstractAnyDataBinder.class);
@@ -144,6 +147,9 @@ abstract class AbstractAnyDataBinder {
@Autowired
protected IntAttrNameParser intAttrNameParser;
+ @Autowired
+ protected OutboundMatcher outboundMatcher;
+
protected void setRealm(final Any<?> any, final AnyUR anyUR) {
if (anyUR.getRealm() != null && StringUtils.isNotBlank(anyUR.getRealm().getValue())) {
Realm newRealm = realmDAO.findByFullPath(anyUR.getRealm().getValue());
@@ -170,10 +176,20 @@ abstract class AbstractAnyDataBinder {
Pair<String, Set<Attribute>> prepared = mappingManager.prepareAttrsFromAny(
any, password, changePwd, true, provision);
- ConnObjectTO connObjectTO = ConnObjectUtils.getConnObjectTO(prepared.getRight());
- connObjectTO.getAttrs().add(
- new Attr.Builder(connObjectKeyItem.getExtAttrName()).value(prepared.getLeft()).build());
- connObjectTO.getAttrs().add(new Attr.Builder(Uid.NAME).value(prepared.getLeft()).build());
+ ConnObjectTO connObjectTO;
+ if (StringUtils.isBlank(prepared.getLeft())) {
+ connObjectTO = ConnObjectUtils.getConnObjectTO(null, prepared.getRight());
+ } else {
+ ConnectorObject connectorObject = new ConnectorObjectBuilder().
+ addAttributes(prepared.getRight()).
+ addAttribute(new Uid(prepared.getLeft())).
+ addAttribute(AttributeBuilder.build(connObjectKeyItem.getExtAttrName(), prepared.getLeft())).
+ build();
+
+ connObjectTO = ConnObjectUtils.getConnObjectTO(
+ outboundMatcher.getFIQL(connectorObject, provision),
+ connectorObject.getAttributes());
+ }
onResources.put(provision.getResource().getKey(), connObjectTO);
}));
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
index 3e3fe7d..3dc37d3 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
@@ -449,9 +449,17 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
taskDAO.save(task);
}
+ String fiql = provision == null
+ ? null
+ : afterObj != null
+ ? outboundMatcher.getFIQL(afterObj, provision)
+ : beforeObj != null
+ ? outboundMatcher.getFIQL(beforeObj, provision)
+ : null;
reporter.onSuccessOrNonPriorityResourceFailures(taskInfo,
ExecStatus.valueOf(execution.getStatus()),
failureReason,
+ fiql,
beforeObj,
afterObj);
}
@@ -532,6 +540,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
ExecStatus.valueOf(execution.getStatus()),
rejectReason,
null,
+ null,
null);
return execution;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationReporter.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationReporter.java
index 5fbb4b0..010daab 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationReporter.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationReporter.java
@@ -50,6 +50,7 @@ public class DefaultPropagationReporter implements PropagationReporter {
final PropagationTaskInfo taskInfo,
final ExecStatus executionStatus,
final String failureReason,
+ final String fiql,
final ConnectorObject beforeObj,
final ConnectorObject afterObj) {
@@ -59,11 +60,11 @@ public class DefaultPropagationReporter implements PropagationReporter {
status.setFailureReason(failureReason);
if (beforeObj != null) {
- status.setBeforeObj(ConnObjectUtils.getConnObjectTO(beforeObj.getAttributes()));
+ status.setBeforeObj(ConnObjectUtils.getConnObjectTO(fiql, beforeObj.getAttributes()));
}
if (afterObj != null) {
- status.setAfterObj(ConnObjectUtils.getConnObjectTO(afterObj.getAttributes()));
+ status.setAfterObj(ConnObjectUtils.getConnObjectTO(fiql, afterObj.getAttributes()));
}
add(status);
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PriorityPropagationTaskExecutor.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PriorityPropagationTaskExecutor.java
index 5af16d1..69e9902 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PriorityPropagationTaskExecutor.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PriorityPropagationTaskExecutor.java
@@ -122,7 +122,7 @@ public class PriorityPropagationTaskExecutor extends AbstractPropagationTaskExec
if (nullPriorityAsync) {
reporter.onSuccessOrNonPriorityResourceFailures(
- taskInfo, ExecStatus.CREATED, null, null, null);
+ taskInfo, ExecStatus.CREATED, null, null, null, null);
}
} catch (Exception e) {
LOG.error("While submitting task for async execution: {}", taskInfo, e);
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/OutboundMatcher.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/OutboundMatcher.java
index 4536fca..c776a0e 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/OutboundMatcher.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/OutboundMatcher.java
@@ -93,6 +93,12 @@ public class OutboundMatcher {
return rule;
}
+ public String getFIQL(final ConnectorObject connectorObject, final Provision provision) {
+ return rule(provision).
+ map(rule -> rule.getFiql(connectorObject, provision)).
+ orElseGet(() -> PushCorrelationRule.DEFAULT_FIQL_BUILDER.apply(connectorObject, provision));
+ }
+
public List<ConnectorObject> match(
final PropagationTask task,
final Connector connector,
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java
index a31d08e..97c67ea 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java
@@ -50,9 +50,6 @@ import org.apache.syncope.core.provisioning.api.pushpull.SyncopeSinglePullExecut
import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
import org.apache.syncope.core.spring.ImplementationManager;
-import org.identityconnectors.framework.common.objects.AttributeBuilder;
-import org.identityconnectors.framework.common.objects.filter.Filter;
-import org.identityconnectors.framework.common.objects.filter.FilterBuilder;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -70,8 +67,7 @@ public class SinglePullJobDelegate extends PullJobDelegate implements SyncopeSin
public List<ProvisioningReport> pull(
final Provision provision,
final Connector connector,
- final String connObjectKey,
- final String connObjectValue,
+ final ReconFilterBuilder reconFilterBuilder,
final PullTaskTO pullTaskTO) throws JobExecutionException {
LOG.debug("Executing pull on {}", provision.getResource());
@@ -160,7 +156,7 @@ public class SinglePullJobDelegate extends PullJobDelegate implements SyncopeSin
connector.filteredReconciliation(
provision.getObjectClass(),
- new SingleReconciliationFilterBuilder(connObjectKey, connObjectValue),
+ reconFilterBuilder,
handler,
MappingUtils.buildOperationOptions(mapItems, moreAttrsToGet.toArray(new String[0])));
@@ -181,21 +177,4 @@ public class SinglePullJobDelegate extends PullJobDelegate implements SyncopeSin
: new JobExecutionException("While pulling from connector", e);
}
}
-
- static class SingleReconciliationFilterBuilder implements ReconFilterBuilder {
-
- private final String key;
-
- private final String value;
-
- SingleReconciliationFilterBuilder(final String key, final String value) {
- this.key = key;
- this.value = value;
- }
-
- @Override
- public Filter build() {
- return FilterBuilder.equalTo(AttributeBuilder.build(key, value));
- }
- }
}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java
index aad7460..4ccbf09 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java
@@ -64,6 +64,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
@Component
public class ConnObjectUtils {
@@ -100,7 +101,7 @@ public class ConnObjectUtils {
* @return password value
*/
public static String getPassword(final Object pwd) {
- final StringBuilder result = new StringBuilder();
+ StringBuilder result = new StringBuilder();
if (pwd instanceof GuardedString) {
result.append(SecurityUtil.decrypt((GuardedString) pwd));
@@ -118,27 +119,31 @@ public class ConnObjectUtils {
/**
* Builds {@link ConnObjectTO} out of a collection of {@link Attribute} instances.
*
+ * @param fiql FIQL expression to uniquely identify the given Connector Object
* @param attrs attributes
* @return transfer object
*/
- public static ConnObjectTO getConnObjectTO(final Set<Attribute> attrs) {
- final ConnObjectTO connObjectTO = new ConnObjectTO();
+ public static ConnObjectTO getConnObjectTO(final String fiql, final Set<Attribute> attrs) {
+ ConnObjectTO connObjectTO = new ConnObjectTO();
+ connObjectTO.setFiql(fiql);
- if (attrs != null) {
+ if (!CollectionUtils.isEmpty(attrs)) {
connObjectTO.getAttrs().addAll(attrs.stream().map(attr -> {
Attr attrTO = new Attr();
attrTO.setSchema(attr.getName());
- if (attr.getValue() != null) {
- attr.getValue().stream().filter(Objects::nonNull).forEachOrdered(value -> {
+
+ if (!CollectionUtils.isEmpty(attr.getValue())) {
+ attr.getValue().stream().filter(Objects::nonNull).forEach(value -> {
if (value instanceof GuardedString || value instanceof GuardedByteArray) {
attrTO.getValues().add(getPassword(value));
} else if (value instanceof byte[]) {
attrTO.getValues().add(Base64.getEncoder().encodeToString((byte[]) value));
- } else if (value != null) {
+ } else {
attrTO.getValues().add(value.toString());
}
});
}
+
return attrTO;
}).collect(Collectors.toList()));
}
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java
index ac13163..1637720 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java
@@ -669,7 +669,7 @@ public class PropagationTaskITCase extends AbstractTaskITCase {
GroupCR groupCR = new GroupCR();
groupCR.setName("SYNCOPEGROUP1605-" + getUUIDString());
- groupCR.setRealm("/");
+ groupCR.setRealm(SyncopeConstants.ROOT_REALM);
groupCR.getResources().add(ldap.getKey());
groupCR.getPlainAttrs().add(new Attr.Builder("originalName").value(originalName).build());
@@ -678,8 +678,7 @@ public class PropagationTaskITCase extends AbstractTaskITCase {
// 3. check attributes prepared for propagation
PagedResult<PropagationTaskTO> tasks = taskService.search(new TaskQuery.Builder(TaskType.PROPAGATION).
- resource(groupTO.getResources().iterator().next()).
- anyTypeKind(AnyTypeKind.GROUP).entityKey(groupTO.getKey()).build());
+ resource(ldap.getKey()).anyTypeKind(AnyTypeKind.GROUP).entityKey(groupTO.getKey()).build());
assertEquals(1, tasks.getSize());
assertEquals(ResourceOperation.CREATE, tasks.getResult().get(0).getOperation());
assertEquals(ExecStatus.SUCCESS.name(), tasks.getResult().get(0).getLatestExecStatus());
@@ -694,8 +693,8 @@ public class PropagationTaskITCase extends AbstractTaskITCase {
groupTO = updateGroup(groupUR).getEntity();
tasks = taskService.search(new TaskQuery.Builder(TaskType.PROPAGATION).
- resource(groupTO.getResources().iterator().next()).
- anyTypeKind(AnyTypeKind.GROUP).entityKey(groupTO.getKey()).orderBy("start DESC").build());
+ resource(ldap.getKey()).anyTypeKind(AnyTypeKind.GROUP).entityKey(groupTO.getKey()).
+ orderBy("start DESC").build());
assertEquals(2, tasks.getSize());
assertEquals(ResourceOperation.UPDATE, tasks.getResult().get(0).getOperation());
assertEquals(ExecStatus.SUCCESS.name(), tasks.getResult().get(0).getLatestExecStatus());
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ReconciliationITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ReconciliationITCase.java
index 529e09b..4642b67 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ReconciliationITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ReconciliationITCase.java
@@ -117,6 +117,10 @@ public class ReconciliationITCase extends AbstractITCase {
if (enable != null) {
status.getOnSyncope().getAttrs().remove(enable);
}
+ // FIQL is always null for Syncope
+ assertNull(status.getOnSyncope().getFiql());
+ assertNotNull(status.getOnResource().getFiql());
+ status.getOnResource().setFiql(null);
assertEquals(status.getOnSyncope(), status.getOnResource());
}
@@ -169,7 +173,7 @@ public class ReconciliationITCase extends AbstractITCase {
// 2. verify reconciliation status
ReconStatus status = reconciliationService.status(
- new ReconQuery.Builder(PRINTER, RESOURCE_NAME_DBSCRIPTED).connObjectKeyValue(externalKey).build());
+ new ReconQuery.Builder(PRINTER, RESOURCE_NAME_DBSCRIPTED).fiql("ID==" + externalKey).build());
assertNotNull(status);
assertNull(status.getAnyTypeKind());
assertNull(status.getAnyKey());
@@ -183,8 +187,8 @@ public class ReconciliationITCase extends AbstractITCase {
PullTaskTO pullTask = new PullTaskTO();
pullTask.setDestinationRealm(SyncopeConstants.ROOT_REALM);
pullTask.setPerformCreate(true);
- reconciliationService.pull(new ReconQuery.Builder(PRINTER, RESOURCE_NAME_DBSCRIPTED).
- connObjectKeyValue(externalKey).build(), pullTask);
+ reconciliationService.pull(
+ new ReconQuery.Builder(PRINTER, RESOURCE_NAME_DBSCRIPTED).fiql("ID==" + externalKey).build(), pullTask);
// 4. verify reconciliation result
AnyObjectTO printer = anyObjectService.read(externalName);