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/12/30 17:09:32 UTC
[syncope] 04/04: [SYNCOPE-1531] Core support
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 4f63cab0c52253d6b6022e39a13b1be8f101a510
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Mon Dec 30 15:13:20 2019 +0100
[SYNCOPE-1531] Core support
---
.../common/rest/api/beans/AbstractCSVSpec.java | 229 ++++++++++++++++++
.../syncope/common/rest/api/beans/CSVPullSpec.java | 136 +++++++++++
.../syncope/common/rest/api/beans/CSVPushSpec.java | 139 +++++++++++
.../rest/api/service/ReconciliationService.java | 33 +++
.../syncope/common/lib/to/AccessTokenTO.java | 1 -
.../syncope/common/lib/to}/ProvisioningReport.java | 2 +-
.../syncope/common/rest/api/RESTHeaders.java | 7 +-
core/idm/logic/pom.xml | 65 +++++
.../syncope/core/logic/ReconciliationLogic.java | 219 ++++++++++++++++-
.../apache/syncope/core/logic}/AbstractTest.java | 14 +-
.../syncope/core/logic/DummyConfParamOps.java} | 27 ++-
.../apache/syncope/core/logic/DummyDomainOps.java | 59 +++++
.../core/logic/DummyImplementationLookup.java | 90 +++++++
.../syncope/core/logic/DummyServiceOps.java} | 29 ++-
.../syncope/core/logic/IdMLogicTestContext.java | 56 +++++
.../core/logic/ReconciliationLogicTest.java | 153 ++++++++++++
.../apache/syncope/core/logic/TestInitializer.java | 70 ++++++
core/idm/logic/src/test/resources/logicTest.xml | 65 +++++
core/idm/logic/src/test/resources/test1.csv | 3 +
.../cxf/service/ReconciliationServiceImpl.java | 43 ++++
.../core/rest/cxf/service/ResourceServiceImpl.java | 3 +-
.../apache/syncope/core/logic/AnyObjectLogic.java | 9 +-
.../org/apache/syncope/core/logic/GroupLogic.java | 18 +-
.../org/apache/syncope/core/logic/TaskLogic.java | 10 +-
.../org/apache/syncope/core/logic/UserLogic.java | 15 +-
.../jpa/dao/MyJPAJSONPlainSchemaDAO.java | 2 +-
.../jpa/dao/PGJPAJSONPlainSchemaDAO.java | 2 +-
.../core/persistence/jpa/dao/AbstractAnyDAO.java | 18 +-
.../persistence/jpa/dao/JPAPlainSchemaDAO.java | 8 +-
.../jpa/entity/resource/JPAExternalResource.java | 8 +-
core/provisioning-api/pom.xml | 29 ++-
.../syncope/core/provisioning/api/Connector.java | 55 ++++-
.../core/provisioning/api}/IntAttrNameParser.java | 3 +-
.../provisioning/api/UserProvisioningManager.java | 2 +-
.../provisioning/api}/jexl/ClassFreeUberspect.java | 2 +-
.../provisioning/api}/jexl/EmptyClassLoader.java | 2 +-
.../core/provisioning/api}/jexl/JexlUtils.java | 20 +-
.../api}/jexl/SyncopeJexlFunctions.java | 2 +-
.../api/propagation/PropagationManager.java | 19 ++
.../api/propagation/PropagationTaskExecutor.java | 4 +-
.../api/propagation/PropagationTaskInfo.java | 65 ++++-
.../api/pushpull/ProvisioningProfile.java | 1 +
.../provisioning/api/pushpull/PullActions.java | 1 +
.../provisioning/api/pushpull/PushActions.java | 1 +
.../api/pushpull/SyncopeSinglePullExecutor.java | 1 +
.../api/pushpull/SyncopeSinglePushExecutor.java | 1 +
.../api/pushpull/stream/StreamConnector.java | 208 ++++++++++++++++
.../SyncopeStreamPullExecutor.java} | 23 +-
.../SyncopeStreamPushExecutor.java} | 26 +-
.../provisioning/api}/IntAttrNameParserTest.java | 117 ++++++++-
core/provisioning-java/pom.xml | 10 +-
.../provisioning/java/ConnectorFacadeProxy.java | 54 +----
.../java/DefaultUserProvisioningManager.java | 2 +-
.../core/provisioning/java/DerAttrHandlerImpl.java | 2 +-
.../core/provisioning/java/MappingManagerImpl.java | 130 ++++++++--
.../provisioning/java/ProvisioningContext.java | 1 +
.../java/data/AbstractAnyDataBinder.java | 17 +-
.../java/data/JEXLItemTransformerImpl.java | 9 +-
.../java/data/NotificationDataBinderImpl.java | 2 +-
.../java/data/ResourceDataBinderImpl.java | 4 +-
.../java/data/SchemaDataBinderImpl.java | 2 +-
.../provisioning/java/data/UserDataBinderImpl.java | 8 +-
.../notification/DefaultNotificationManager.java | 4 +-
.../AbstractPropagationTaskExecutor.java | 31 ++-
.../LDAPMembershipPropagationActions.java | 18 +-
.../PriorityPropagationTaskExecutor.java | 49 +---
.../java/propagation/PropagationManagerImpl.java | 75 +++---
.../pushpull/AbstractProvisioningJobDelegate.java | 114 +++++----
.../java/pushpull/AbstractPullResultHandler.java | 18 +-
.../java/pushpull/AbstractPushResultHandler.java | 22 +-
.../java/pushpull/DBPasswordPullActions.java | 2 +-
.../DefaultAnyObjectPullResultHandler.java | 2 +-
.../pushpull/DefaultGroupPullResultHandler.java | 2 +-
.../pushpull/DefaultRealmPullResultHandler.java | 2 +-
.../pushpull/DefaultRealmPushResultHandler.java | 2 +-
.../pushpull/DefaultUserPullResultHandler.java | 9 +-
.../pushpull/DefaultUserPushResultHandler.java | 2 +-
.../provisioning/java/pushpull/InboundMatcher.java | 16 +-
.../java/pushpull/LDAPMembershipPullActions.java | 2 +-
.../java/pushpull/LDAPPasswordPullActions.java | 2 +-
.../java/pushpull/OutboundMatcher.java | 2 +-
.../java/pushpull/PushJobDelegate.java | 8 +-
.../java/pushpull/SinglePullJobDelegate.java | 10 +-
.../java/pushpull/SinglePushJobDelegate.java | 2 +-
.../stream/StreamAnyObjectPushResultHandler.java | 73 ++++++
.../stream/StreamGroupPushResultHandler.java | 73 ++++++
.../pushpull/stream/StreamPullJobDelegate.java | 264 +++++++++++++++++++++
.../pushpull/stream/StreamPushJobDelegate.java | 200 ++++++++++++++++
.../stream/StreamUserPushResultHandler.java | 73 ++++++
.../core/provisioning/java/utils/MappingUtils.java | 85 -------
.../provisioning/java/utils/TemplateUtils.java | 2 +-
.../core/provisioning/java/AbstractTest.java | 12 +
.../java/{ => data}/ResourceDataBinderTest.java | 3 +-
.../java/{ => jexl}/MailTemplateTest.java | 6 +-
.../provisioning/java/{ => jexl}/MappingTest.java | 22 +-
.../pushpull/stream/StreamPullJobDelegateTest.java | 117 +++++++++
.../pushpull/stream/StreamPushJobDelegateTest.java | 119 ++++++++++
.../camel/CamelUserProvisioningManager.java | 2 +-
.../syncope/core/logic/oidc/OIDCUserManager.java | 2 +-
.../java/data/OIDCProviderDataBinderImpl.java | 4 +-
.../syncope/core/logic/saml2/SAML2UserManager.java | 2 +-
.../java/data/SAML2IdPDataBinderImpl.java | 4 +-
fit/core-reference/pom.xml | 94 ++++++++
.../apache/syncope/fit/core/PullTaskITCase.java | 15 +-
.../syncope/fit/core/ReconciliationITCase.java | 126 +++++++++-
.../org/apache/syncope/fit/core/UserITCase.java | 7 +-
.../org/apache/syncope/fit/core/VirAttrITCase.java | 13 +-
pom.xml | 23 +-
108 files changed, 3535 insertions(+), 556 deletions(-)
diff --git a/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/AbstractCSVSpec.java b/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/AbstractCSVSpec.java
new file mode 100644
index 0000000..7844335
--- /dev/null
+++ b/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/AbstractCSVSpec.java
@@ -0,0 +1,229 @@
+/*
+ * 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.rest.api.beans;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.QueryParam;
+import org.apache.syncope.common.lib.types.MatchingRule;
+import org.apache.syncope.common.lib.types.UnmatchingRule;
+
+public abstract class AbstractCSVSpec {
+
+ protected abstract static class Builder<T extends AbstractCSVSpec, B extends Builder<T, B>> {
+
+ protected T instance;
+
+ protected abstract T newInstance();
+
+ protected T getInstance() {
+ if (instance == null) {
+ instance = newInstance();
+ }
+ return instance;
+ }
+
+ @SuppressWarnings("unchecked")
+ public B columnSeparator(final char columnSeparator) {
+ getInstance().setColumnSeparator(columnSeparator);
+ return (B) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ public B arrayElementSeparator(final String arrayElementSeparator) {
+ getInstance().setArrayElementSeparator(arrayElementSeparator);
+ return (B) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ public B quoteChar(final char quoteChar) {
+ getInstance().setQuoteChar(quoteChar);
+ return (B) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ public B escapeChar(final char escapeChar) {
+ getInstance().setEscapeChar(escapeChar);
+ return (B) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ public B lineSeparator(final String lineSeparatorChar) {
+ getInstance().setLineSeparator(lineSeparatorChar);
+ return (B) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ public B nullValue(final String nullValue) {
+ getInstance().setNullValue(nullValue);
+ return (B) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ public B allowComments(final boolean allowComments) {
+ getInstance().setAllowComments(allowComments);
+ return (B) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ public B unmatchingRule(final UnmatchingRule unmatchingRule) {
+ getInstance().setUnmatchingRule(unmatchingRule);
+ return (B) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ public B matchingRule(final MatchingRule matchingRule) {
+ getInstance().setMatchingRule(matchingRule);
+ return (B) this;
+ }
+
+ @SuppressWarnings("unchecked")
+ public B action(final String action) {
+ getInstance().getActions().add(action);
+ return (B) this;
+ }
+
+ public T build() {
+ return getInstance();
+ }
+ }
+
+ protected String anyTypeKey;
+
+ protected char columnSeparator = ',';
+
+ protected String arrayElementSeparator = ";";
+
+ protected char quoteChar = '"';
+
+ protected Character escapeChar;
+
+ protected String lineSeparator = "\n";
+
+ protected String nullValue = "";
+
+ protected boolean allowComments;
+
+ protected UnmatchingRule unmatchingRule = UnmatchingRule.PROVISION;
+
+ protected MatchingRule matchingRule = MatchingRule.UPDATE;
+
+ protected List<String> actions = new ArrayList<>();
+
+ public String getAnyTypeKey() {
+ return anyTypeKey;
+ }
+
+ @NotNull
+ @QueryParam("anyTypeKey")
+ public void setAnyTypeKey(final String anyTypeKey) {
+ this.anyTypeKey = anyTypeKey;
+ }
+
+ public char getColumnSeparator() {
+ return columnSeparator;
+ }
+
+ @QueryParam("columnSeparator")
+ public void setColumnSeparator(final char columnSeparator) {
+ this.columnSeparator = columnSeparator;
+ }
+
+ public String getArrayElementSeparator() {
+ return arrayElementSeparator;
+ }
+
+ @QueryParam("arrayElementSeparator")
+ public void setArrayElementSeparator(final String arrayElementSeparator) {
+ this.arrayElementSeparator = arrayElementSeparator;
+ }
+
+ public char getQuoteChar() {
+ return quoteChar;
+ }
+
+ @QueryParam("quoteChar")
+ public void setQuoteChar(final char quoteChar) {
+ this.quoteChar = quoteChar;
+ }
+
+ public Character getEscapeChar() {
+ return escapeChar;
+ }
+
+ @QueryParam("escapeChar")
+ public void setEscapeChar(final Character escapeChar) {
+ this.escapeChar = escapeChar;
+ }
+
+ public String getLineSeparator() {
+ return lineSeparator;
+ }
+
+ @QueryParam("lineSeparator")
+ public void setLineSeparator(final String lineSeparator) {
+ this.lineSeparator = lineSeparator;
+ }
+
+ public String getNullValue() {
+ return nullValue;
+ }
+
+ @QueryParam("nullValue")
+ public void setNullValue(final String nullValue) {
+ this.nullValue = nullValue;
+ }
+
+ public boolean isAllowComments() {
+ return allowComments;
+ }
+
+ @QueryParam("allowComments")
+ public void setAllowComments(final boolean allowComments) {
+ this.allowComments = allowComments;
+ }
+
+ public UnmatchingRule getUnmatchingRule() {
+ return unmatchingRule;
+ }
+
+ @QueryParam("unmatchingRule")
+ public void setUnmatchingRule(final UnmatchingRule unmatchingRule) {
+ this.unmatchingRule = unmatchingRule;
+ }
+
+ public MatchingRule getMatchingRule() {
+ return matchingRule;
+ }
+
+ @QueryParam("matchingRule")
+ public void setMatchingRule(final MatchingRule matchingRule) {
+ this.matchingRule = matchingRule;
+ }
+
+ public List<String> getActions() {
+ return actions;
+ }
+
+ @QueryParam("actions")
+ public void setActions(final List<String> actions) {
+ this.actions = actions;
+ }
+}
diff --git a/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/CSVPullSpec.java b/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/CSVPullSpec.java
new file mode 100644
index 0000000..7bb8a2d
--- /dev/null
+++ b/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/CSVPullSpec.java
@@ -0,0 +1,136 @@
+/*
+ * 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.rest.api.beans;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.QueryParam;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.types.ConflictResolutionAction;
+
+public class CSVPullSpec extends AbstractCSVSpec {
+
+ public static class Builder extends AbstractCSVSpec.Builder<CSVPullSpec, Builder> {
+
+ @Override
+ protected CSVPullSpec newInstance() {
+ return new CSVPullSpec();
+ }
+
+ public Builder(final String anyTypeKey, final String keyColumn) {
+ getInstance().setAnyTypeKey(anyTypeKey);
+ getInstance().setKeyColumn(keyColumn);
+ }
+
+ public Builder remediation(final boolean remediation) {
+ instance.setRemediation(remediation);
+ return this;
+ }
+
+ public Builder ignoreColumns(final String... ignoreColumns) {
+ instance.getIgnoreColumns().addAll(Stream.of(ignoreColumns).collect(Collectors.toList()));
+ return this;
+ }
+
+ public Builder destinationRealm(final String destinationRealm) {
+ instance.setDestinationRealm(destinationRealm);
+ return this;
+ }
+
+ public Builder conflictResolutionAction(final ConflictResolutionAction conflictResolutionAction) {
+ instance.setConflictResolutionAction(conflictResolutionAction);
+ return this;
+ }
+
+ public Builder pullCorrelationRule(final String pullCorrelationRule) {
+ instance.setPullCorrelationRule(pullCorrelationRule);
+ return this;
+ }
+ }
+
+ private String destinationRealm = SyncopeConstants.ROOT_REALM;
+
+ private String keyColumn;
+
+ private Set<String> ignoreColumns = new HashSet<>();
+
+ private boolean remediation;
+
+ private ConflictResolutionAction conflictResolutionAction = ConflictResolutionAction.IGNORE;
+
+ private String pullCorrelationRule;
+
+ public String getDestinationRealm() {
+ return destinationRealm;
+ }
+
+ @QueryParam("destinationRealm")
+ public void setDestinationRealm(final String destinationRealm) {
+ this.destinationRealm = destinationRealm;
+ }
+
+ public String getKeyColumn() {
+ return keyColumn;
+ }
+
+ @NotNull
+ @QueryParam("keyColumn")
+ public void setKeyColumn(final String keyColumn) {
+ this.keyColumn = keyColumn;
+ }
+
+ public Set<String> getIgnoreColumns() {
+ return ignoreColumns;
+ }
+
+ @QueryParam("ignoreColumns")
+ public void setIgnoreColumns(final Set<String> ignoreColumns) {
+ this.ignoreColumns = ignoreColumns;
+ }
+
+ public boolean isRemediation() {
+ return remediation;
+ }
+
+ @QueryParam("remediation")
+ public void setRemediation(final boolean remediation) {
+ this.remediation = remediation;
+ }
+
+ public ConflictResolutionAction getConflictResolutionAction() {
+ return conflictResolutionAction;
+ }
+
+ @QueryParam("conflictResolutionAction")
+ public void setConflictResolutionAction(final ConflictResolutionAction conflictResolutionAction) {
+ this.conflictResolutionAction = conflictResolutionAction;
+ }
+
+ public String getPullCorrelationRule() {
+ return pullCorrelationRule;
+ }
+
+ @QueryParam("pullCorrelationRule")
+ public void setPullCorrelationRule(final String pullCorrelationRule) {
+ this.pullCorrelationRule = pullCorrelationRule;
+ }
+}
diff --git a/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/CSVPushSpec.java b/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/CSVPushSpec.java
new file mode 100644
index 0000000..92b2196
--- /dev/null
+++ b/common/idm/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/CSVPushSpec.java
@@ -0,0 +1,139 @@
+/*
+ * 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.rest.api.beans;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import javax.ws.rs.QueryParam;
+
+public class CSVPushSpec extends AbstractCSVSpec {
+
+ public static class Builder extends AbstractCSVSpec.Builder<CSVPushSpec, Builder> {
+
+ @Override
+ protected CSVPushSpec newInstance() {
+ return new CSVPushSpec();
+ }
+
+ public Builder(final String anyTypeKey) {
+ getInstance().setAnyTypeKey(anyTypeKey);
+ }
+
+ public Builder field(final String field) {
+ getInstance().getFields().add(field);
+ return this;
+ }
+
+ public Builder fields(final Collection<String> fields) {
+ getInstance().getFields().addAll(fields);
+ return this;
+ }
+
+ public Builder plainAttr(final String plainAttr) {
+ getInstance().getPlainAttrs().add(plainAttr);
+ return this;
+ }
+
+ public Builder plainAttrs(final Collection<String> plainAttrs) {
+ getInstance().getPlainAttrs().addAll(plainAttrs);
+ return this;
+ }
+
+ public Builder derAttr(final String derAttr) {
+ getInstance().getDerAttrs().add(derAttr);
+ return this;
+ }
+
+ public Builder derAttrs(final Collection<String> derAttrs) {
+ getInstance().getDerAttrs().addAll(derAttrs);
+ return this;
+ }
+
+ public Builder virAttr(final String virAttr) {
+ getInstance().getVirAttrs().add(virAttr);
+ return this;
+ }
+
+ public Builder virAttrs(final Collection<String> virAttrs) {
+ getInstance().getVirAttrs().addAll(virAttrs);
+ return this;
+ }
+
+ public Builder ignorePagination(final boolean ignorePagination) {
+ getInstance().setIgnorePaging(ignorePagination);
+ return this;
+ }
+ }
+
+ private List<String> fields = new ArrayList<>();
+
+ private List<String> plainAttrs = new ArrayList<>();
+
+ private List<String> derAttrs = new ArrayList<>();
+
+ private List<String> virAttrs = new ArrayList<>();
+
+ private boolean ignorePaging;
+
+ public List<String> getFields() {
+ return fields;
+ }
+
+ @QueryParam("fields")
+ public void setFields(final List<String> fields) {
+ this.fields = fields;
+ }
+
+ public List<String> getPlainAttrs() {
+ return plainAttrs;
+ }
+
+ @QueryParam("plainAttrs")
+ public void setPlainAttrs(final List<String> plainAttrs) {
+ this.plainAttrs = plainAttrs;
+ }
+
+ public List<String> getDerAttrs() {
+ return derAttrs;
+ }
+
+ @QueryParam("derAttrs")
+ public void setDerAttrs(final List<String> derAttrs) {
+ this.derAttrs = derAttrs;
+ }
+
+ public List<String> getVirAttrs() {
+ return virAttrs;
+ }
+
+ @QueryParam("virAttrs")
+ public void setVirAttrs(final List<String> virAttrs) {
+ this.virAttrs = virAttrs;
+ }
+
+ public boolean isIgnorePaging() {
+ return ignorePaging;
+ }
+
+ @QueryParam("ignorePaging")
+ public void setIgnorePaging(final boolean ignorePaging) {
+ this.ignorePaging = ignorePaging;
+ }
+}
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 cb44ef9..a2c8193 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
@@ -23,6 +23,8 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.security.SecurityRequirements;
import io.swagger.v3.oas.annotations.tags.Tag;
+import java.io.InputStream;
+import java.util.List;
import javax.validation.constraints.NotNull;
import javax.ws.rs.BeanParam;
import javax.ws.rs.Consumes;
@@ -31,10 +33,15 @@ import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+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.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.beans.AnyQuery;
+import org.apache.syncope.common.rest.api.beans.CSVPullSpec;
+import org.apache.syncope.common.rest.api.beans.CSVPushSpec;
import org.apache.syncope.common.rest.api.beans.ReconQuery;
/**
@@ -84,4 +91,30 @@ public interface ReconciliationService extends JAXRSService {
@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);
+
+ /**
+ * Export a list of any objects matching the given query as CSV according to the provided specification.
+ *
+ * @param anyQuery query conditions
+ * @param spec CSV push specification
+ * @return CSV content matching the provided specification
+ */
+ @GET
+ @Path("csv/push")
+ @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+ @Produces({ RESTHeaders.TEXT_CSV })
+ Response push(@BeanParam AnyQuery anyQuery, @BeanParam CSVPushSpec spec);
+
+ /**
+ * Pulls the CSV input into Syncope according to the provided specification.
+ *
+ * @param spec CSV pull specification
+ * @param csv CSV input
+ * @return pull report
+ */
+ @POST
+ @Path("csv/pull")
+ @Consumes({ RESTHeaders.TEXT_CSV })
+ @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+ List<ProvisioningReport> pull(@BeanParam CSVPullSpec spec, InputStream csv);
}
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AccessTokenTO.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AccessTokenTO.java
index e3e00f6..501ecb9 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AccessTokenTO.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AccessTokenTO.java
@@ -72,5 +72,4 @@ public class AccessTokenTO extends BaseBean implements EntityTO {
public void setOwner(final String owner) {
this.owner = owner;
}
-
}
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningReport.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/ProvisioningReport.java
similarity index 98%
rename from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningReport.java
rename to common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/ProvisioningReport.java
index 3c0a6f0..216ab56 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningReport.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/ProvisioningReport.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.provisioning.api.pushpull;
+package org.apache.syncope.common.lib.to;
import java.util.Collection;
import org.apache.commons.lang3.StringUtils;
diff --git a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/RESTHeaders.java b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/RESTHeaders.java
index 0429e29..fdabce0 100644
--- a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/RESTHeaders.java
+++ b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/RESTHeaders.java
@@ -68,6 +68,11 @@ public final class RESTHeaders {
public static final MediaType APPLICATION_YAML_TYPE = new MediaType("application", "yaml");
/**
+ * Mediatype for text/csv, not defined in {@link javax.ws.rs.core.MediaType}.
+ */
+ public static final String TEXT_CSV = "text/csv";
+
+ /**
* Mediatype for multipart/mixed, not defined in {@link javax.ws.rs.core.MediaType}.
*/
public static final String MULTIPART_MIXED = "multipart/mixed";
@@ -79,7 +84,7 @@ public final class RESTHeaders {
/**
* Builds Content-Type string for multipart/mixed and the given boundary.
- *
+ *
* @param boundary multipart boundary value
* @return multipart/mixed Content-Type string, with given boundary
*/
diff --git a/core/idm/logic/pom.xml b/core/idm/logic/pom.xml
index c34b10c..baf271c 100644
--- a/core/idm/logic/pom.xml
+++ b/core/idm/logic/pom.xml
@@ -39,6 +39,11 @@ under the License.
<dependencies>
<dependency>
+ <groupId>com.fasterxml.jackson.dataformat</groupId>
+ <artifactId>jackson-dataformat-csv</artifactId>
+ </dependency>
+
+ <dependency>
<groupId>org.apache.syncope.core.idrepo</groupId>
<artifactId>syncope-core-idrepo-logic</artifactId>
<version>${project.version}</version>
@@ -48,6 +53,45 @@ under the License.
<artifactId>syncope-common-idm-rest-api</artifactId>
<version>${project.version}</version>
</dependency>
+
+ <dependency>
+ <groupId>org.apache.syncope.core</groupId>
+ <artifactId>syncope-core-workflow-java</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.el</groupId>
+ <artifactId>javax.el-api</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.syncope.core</groupId>
+ <artifactId>syncope-core-persistence-jpa</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ <version>${slf4j.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.h2database</groupId>
+ <artifactId>h2</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
@@ -57,6 +101,27 @@ under the License.
<filtering>true</filtering>
</resource>
</resources>
+ <testResources>
+ <testResource>
+ <directory>${basedir}/src/test/resources</directory>
+ <filtering>true</filtering>
+ </testResource>
+ <testResource>
+ <directory>${basedir}/../../persistence-jpa/src/main/resources</directory>
+ <includes>
+ <include>persistence.properties</include>
+ </includes>
+ <filtering>true</filtering>
+ </testResource>
+ <testResource>
+ <directory>${basedir}/../../persistence-jpa/src/test/resources</directory>
+ <filtering>true</filtering>
+ </testResource>
+ <testResource>
+ <directory>${basedir}/../../provisioning-java/src/test/resources</directory>
+ <filtering>true</filtering>
+ </testResource>
+ </testResources>
<plugins>
<plugin>
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 2c9fd4b..388f82b 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
@@ -18,9 +18,16 @@
*/
package org.apache.syncope.core.logic;
+import com.fasterxml.jackson.databind.MappingIterator;
+import com.fasterxml.jackson.databind.SequenceWriter;
+import com.fasterxml.jackson.dataformat.csv.CsvMapper;
+import com.fasterxml.jackson.dataformat.csv.CsvSchema;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
@@ -35,9 +42,8 @@ import org.apache.syncope.common.lib.to.PushTaskTO;
import org.apache.syncope.common.lib.to.ReconStatus;
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.common.lib.types.IdRepoEntitlement;
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;
@@ -54,13 +60,30 @@ import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount;
import org.apache.syncope.core.provisioning.api.ConnectorFactory;
import org.apache.syncope.core.provisioning.api.MappingManager;
import org.apache.syncope.core.provisioning.api.VirAttrHandler;
-import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
+import org.apache.syncope.common.lib.types.AnyEntitlement;
+import org.apache.syncope.common.lib.types.IdMEntitlement;
+import org.apache.syncope.common.lib.types.IdRepoEntitlement;
+import org.apache.syncope.common.rest.api.beans.AbstractCSVSpec;
+import org.apache.syncope.common.rest.api.beans.CSVPushSpec;
+import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
+import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+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.provisioning.api.pushpull.stream.StreamConnector;
import org.apache.syncope.core.provisioning.api.pushpull.SyncopeSinglePullExecutor;
import org.apache.syncope.core.provisioning.api.pushpull.SyncopeSinglePushExecutor;
+import org.apache.syncope.core.provisioning.api.pushpull.stream.SyncopeStreamPullExecutor;
+import org.apache.syncope.core.provisioning.api.pushpull.stream.SyncopeStreamPushExecutor;
+import org.apache.syncope.core.provisioning.api.utils.RealmUtils;
import org.apache.syncope.core.provisioning.java.pushpull.InboundMatcher;
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.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.Uid;
@@ -85,6 +108,18 @@ public class ReconciliationLogic extends AbstractTransactionalLogic<EntityTO> {
private RealmDAO realmDAO;
@Autowired
+ private PlainSchemaDAO plainSchemaDAO;
+
+ @Autowired
+ private DerSchemaDAO derSchemaDAO;
+
+ @Autowired
+ private VirSchemaDAO virSchemaDAO;
+
+ @Autowired
+ private AnySearchDAO searchDAO;
+
+ @Autowired
private VirAttrHandler virAttrHandler;
@Autowired
@@ -105,6 +140,12 @@ public class ReconciliationLogic extends AbstractTransactionalLogic<EntityTO> {
@Autowired
private SyncopeSinglePushExecutor singlePushExecutor;
+ @Autowired
+ private SyncopeStreamPushExecutor streamPushExecutor;
+
+ @Autowired
+ private SyncopeStreamPullExecutor streamPullExecutor;
+
private Provision getProvision(final String anyTypeKey, final String resourceKey) {
AnyType anyType = anyTypeDAO.find(anyTypeKey);
if (anyType == null) {
@@ -337,6 +378,178 @@ public class ReconciliationLogic extends AbstractTransactionalLogic<EntityTO> {
}
}
+ private CsvSchema csvSchema(final AbstractCSVSpec spec, final CsvSchema base) {
+ CsvSchema schema = base.
+ withColumnSeparator(spec.getColumnSeparator()).
+ withArrayElementSeparator(spec.getArrayElementSeparator()).
+ withQuoteChar(spec.getQuoteChar()).
+ withLineSeparator(spec.getLineSeparator()).
+ withNullValue(spec.getNullValue()).
+ withAllowComments(spec.isAllowComments());
+ if (spec.getEscapeChar() != null) {
+ schema = schema.withEscapeChar(spec.getEscapeChar());
+ }
+
+ return schema;
+ }
+
+ @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_EXECUTE + "')")
+ public List<ProvisioningReport> push(
+ final SearchCond searchCond,
+ final int page,
+ final int size,
+ final List<OrderByClause> orderBy,
+ final String realm,
+ final CSVPushSpec spec,
+ final OutputStream os) {
+
+ AnyType anyType = anyTypeDAO.find(spec.getAnyTypeKey());
+ if (anyType == null) {
+ throw new NotFoundException("AnyType '" + spec.getAnyTypeKey() + "'");
+ }
+
+ AnyUtils anyUtils = anyUtilsFactory.getInstance(anyType.getKind());
+
+ String entitlement;
+ switch (anyType.getKind()) {
+ case GROUP:
+ entitlement = IdRepoEntitlement.GROUP_SEARCH;
+ break;
+
+ case ANY_OBJECT:
+ entitlement = AnyEntitlement.SEARCH.getFor(anyType.getKey());
+ break;
+
+ case USER:
+ default:
+ entitlement = IdRepoEntitlement.USER_SEARCH;
+ }
+
+ Set<String> adminRealms = RealmUtils.getEffective(AuthContextUtils.getAuthorizations().get(entitlement), realm);
+ SearchCond effectiveCond = searchCond == null ? anyUtils.dao().getAllMatchingCond() : searchCond;
+
+ List<Any<?>> matching;
+ if (spec.isIgnorePaging()) {
+ matching = new ArrayList<>();
+
+ int count = searchDAO.count(adminRealms, searchCond, anyType.getKind());
+ int pages = (count / AnyDAO.DEFAULT_PAGE_SIZE) + 1;
+
+ for (int p = 1; p <= pages; p++) {
+ matching.addAll(searchDAO.search(adminRealms, effectiveCond,
+ p, AnyDAO.DEFAULT_PAGE_SIZE, orderBy, anyType.getKind()));
+ }
+ } else {
+ matching = searchDAO.search(adminRealms, effectiveCond, page, size, orderBy, anyType.getKind());
+ }
+
+ List<String> columns = new ArrayList<>();
+ spec.getFields().forEach(item -> {
+ if (anyUtils.getField(item) == null) {
+ LOG.warn("Ignoring invalid field {}", item);
+ } else {
+ columns.add(item);
+ }
+ });
+ spec.getPlainAttrs().forEach(item -> {
+ if (plainSchemaDAO.find(item) == null) {
+ LOG.warn("Ignoring invalid plain schema {}", item);
+ } else {
+ columns.add(item);
+ }
+ });
+ spec.getDerAttrs().forEach(item -> {
+ if (derSchemaDAO.find(item) == null) {
+ LOG.warn("Ignoring invalid derived schema {}", item);
+ } else {
+ columns.add(item);
+ }
+ });
+ spec.getVirAttrs().forEach(item -> {
+ if (virSchemaDAO.find(item) == null) {
+ LOG.warn("Ignoring invalid virtual schema {}", item);
+ } else {
+ columns.add(item);
+ }
+ });
+
+ CsvSchema.Builder schemaBuilder = CsvSchema.builder().setUseHeader(true);
+ columns.forEach(schemaBuilder::addColumn);
+ CsvSchema schema = csvSchema(spec, schemaBuilder.build());
+
+ PushTaskTO pushTask = new PushTaskTO();
+ pushTask.setMatchingRule(spec.getMatchingRule());
+ pushTask.setUnmatchingRule(spec.getUnmatchingRule());
+ pushTask.getActions().addAll(spec.getActions());
+
+ try (SequenceWriter writer = new CsvMapper().writer(schema).forType(Map.class).writeValues(os)) {
+ return streamPushExecutor.push(
+ anyType,
+ matching,
+ columns,
+ new StreamConnector(null, spec.getArrayElementSeparator(), null, writer),
+ pushTask,
+ AuthContextUtils.getUsername());
+ } catch (Exception e) {
+ LOG.error("Could not push to stream", e);
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Reconciliation);
+ sce.getElements().add(e.getMessage());
+ throw sce;
+ }
+ }
+
+ @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_EXECUTE + "')")
+ public List<ProvisioningReport> pull(final CSVPullSpec spec, final InputStream csv) {
+ AnyType anyType = anyTypeDAO.find(spec.getAnyTypeKey());
+ if (anyType == null) {
+ throw new NotFoundException("AnyType '" + spec.getAnyTypeKey() + "'");
+ }
+
+ if (realmDAO.findByFullPath(spec.getDestinationRealm()) == null) {
+ throw new NotFoundException("Realm " + spec.getDestinationRealm());
+ }
+
+ PullTaskTO pullTask = new PullTaskTO();
+ pullTask.setDestinationRealm(spec.getDestinationRealm());
+ pullTask.setRemediation(spec.isRemediation());
+ pullTask.setMatchingRule(spec.getMatchingRule());
+ pullTask.setUnmatchingRule(spec.getUnmatchingRule());
+ pullTask.getActions().addAll(spec.getActions());
+
+ CsvSchema schema = csvSchema(spec, CsvSchema.emptySchema().withHeader());
+ try {
+ MappingIterator<Map<String, String>> reader =
+ new CsvMapper().readerFor(Map.class).with(schema).readValues(csv);
+
+ List<String> columns = new ArrayList<>();
+ ((CsvSchema) reader.getParserSchema()).forEach(column -> {
+ if (!spec.getIgnoreColumns().contains(column.getName())) {
+ columns.add(column.getName());
+ }
+ });
+
+ if (!columns.contains(spec.getKeyColumn())) {
+ throw new NotFoundException("Key column '" + spec.getKeyColumn() + "'");
+ }
+
+ return streamPullExecutor.pull(
+ anyType,
+ spec.getKeyColumn(),
+ columns,
+ spec.getConflictResolutionAction(),
+ spec.getPullCorrelationRule(),
+ new StreamConnector(spec.getKeyColumn(), spec.getArrayElementSeparator(), reader, null),
+ pullTask);
+ } catch (NotFoundException e) {
+ throw e;
+ } catch (Exception e) {
+ LOG.error("Could not pull from stream", e);
+ SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Reconciliation);
+ sce.getElements().add(e.getMessage());
+ throw sce;
+ }
+ }
+
@Override
protected EntityTO resolveReference(final Method method, final Object... os)
throws UnresolvedReferenceException {
diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/AbstractTest.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/AbstractTest.java
similarity index 75%
copy from core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/AbstractTest.java
copy to core/idm/logic/src/test/java/org/apache/syncope/core/logic/AbstractTest.java
index b120f93..424f348 100644
--- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/AbstractTest.java
+++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/AbstractTest.java
@@ -16,15 +16,19 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.provisioning.java;
+package org.apache.syncope.core.logic;
import javax.persistence.EntityManager;
+import org.apache.syncope.common.lib.types.EntitlementsHolder;
+import org.apache.syncope.common.lib.types.IdMEntitlement;
+import org.apache.syncope.common.lib.types.IdRepoEntitlement;
import org.apache.syncope.core.spring.ApplicationContextProvider;
import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.junit.jupiter.api.BeforeAll;
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
-@SpringJUnitConfig(classes = { ProvisioningTestContext.class })
+@SpringJUnitConfig(classes = { IdMLogicTestContext.class })
public abstract class AbstractTest {
protected EntityManager entityManager() {
@@ -37,4 +41,10 @@ public abstract class AbstractTest {
return entityManager;
}
+
+ @BeforeAll
+ public static void init() {
+ EntitlementsHolder.getInstance().addAll(IdRepoEntitlement.values());
+ EntitlementsHolder.getInstance().addAll(IdMEntitlement.values());
+ }
}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/EmptyClassLoader.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyConfParamOps.java
similarity index 56%
copy from core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/EmptyClassLoader.java
copy to core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyConfParamOps.java
index 037113e..ca3a1ac 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/EmptyClassLoader.java
+++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyConfParamOps.java
@@ -16,21 +16,30 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.provisioning.java.jexl;
+package org.apache.syncope.core.logic;
-/**
- * A class loader that will throw {@link ClassNotFoundException} for every class name.
- */
-class EmptyClassLoader extends ClassLoader {
+import java.util.Map;
+import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DummyConfParamOps implements ConfParamOps {
@Override
- public Class<?> loadClass(final String name) throws ClassNotFoundException {
- throw new ClassNotFoundException("This classloader won't attemp to load " + name);
+ public Map<String, Object> list(final String domain) {
+ return Map.of();
}
@Override
- protected Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {
- throw new ClassNotFoundException("This classloader won't attemp to load " + name);
+ public <T> T get(final String domain, final String key, final T defaultValue, final Class<T> reference) {
+ return defaultValue;
}
+ @Override
+ public <T> void set(final String domain, final String key, final T value) {
+ }
+
+ @Override
+ public void remove(final String domain, final String key) {
+ }
}
diff --git a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyDomainOps.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyDomainOps.java
new file mode 100644
index 0000000..09757b0
--- /dev/null
+++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyDomainOps.java
@@ -0,0 +1,59 @@
+/*
+ * 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.logic;
+
+import java.util.List;
+import org.apache.syncope.common.keymaster.client.api.DomainOps;
+import org.apache.syncope.common.keymaster.client.api.model.Domain;
+import org.apache.syncope.common.lib.types.CipherAlgorithm;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DummyDomainOps implements DomainOps {
+
+ @Override
+ public List<Domain> list() {
+ return List.of();
+ }
+
+ @Override
+ public Domain read(final String key) {
+ return new Domain.Builder(key).build();
+ }
+
+ @Override
+ public void create(final Domain domain) {
+ // nothing to do
+ }
+
+ @Override
+ public void changeAdminPassword(final String key, final String password, final CipherAlgorithm cipherAlgorithm) {
+ // nothing to do
+ }
+
+ @Override
+ public void adjustPoolSize(final String key, final int maxPoolSize, final int minIdle) {
+ // nothing to do
+ }
+
+ @Override
+ public void delete(final String key) {
+ // nothing to do
+ }
+}
diff --git a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyImplementationLookup.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyImplementationLookup.java
new file mode 100644
index 0000000..7cf8a15
--- /dev/null
+++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyImplementationLookup.java
@@ -0,0 +1,90 @@
+/*
+ * 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.logic;
+
+import java.util.Set;
+import org.apache.syncope.common.lib.policy.AccountRuleConf;
+import org.apache.syncope.common.lib.policy.PasswordRuleConf;
+import org.apache.syncope.common.lib.policy.PullCorrelationRuleConf;
+import org.apache.syncope.common.lib.policy.PushCorrelationRuleConf;
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.core.persistence.api.ImplementationLookup;
+import org.apache.syncope.core.persistence.api.dao.AccountRule;
+import org.apache.syncope.core.persistence.api.dao.PasswordRule;
+import org.apache.syncope.core.persistence.api.dao.PullCorrelationRule;
+import org.apache.syncope.core.persistence.api.dao.PushCorrelationRule;
+import org.apache.syncope.core.persistence.api.dao.Reportlet;
+
+public class DummyImplementationLookup implements ImplementationLookup {
+
+ @Override
+ public int getOrder() {
+ return -1;
+ }
+
+ @Override
+ public Set<String> getClassNames(final String type) {
+ return Set.of();
+ }
+
+ @Override
+ public Set<Class<?>> getJWTSSOProviderClasses() {
+ return Set.of();
+ }
+
+ @Override
+ public Class<Reportlet> getReportletClass(
+ final Class<? extends ReportletConf> reportletConfClass) {
+
+ return null;
+ }
+
+ @Override
+ public Class<? extends AccountRule> getAccountRuleClass(
+ final Class<? extends AccountRuleConf> accountRuleConfClass) {
+
+ return null;
+ }
+
+ @Override
+ public Class<? extends PasswordRule> getPasswordRuleClass(
+ final Class<? extends PasswordRuleConf> passwordRuleConfClass) {
+
+ return null;
+ }
+
+ @Override
+ public Class<? extends PullCorrelationRule> getPullCorrelationRuleClass(
+ final Class<? extends PullCorrelationRuleConf> pullCorrelationRuleConfClass) {
+
+ return null;
+ }
+
+ @Override
+ public Class<? extends PushCorrelationRule> getPushCorrelationRuleClass(
+ final Class<? extends PushCorrelationRuleConf> pushCorrelationRuleConfClass) {
+
+ return null;
+ }
+
+ @Override
+ public Set<Class<?>> getAuditAppenderClasses() {
+ return Set.of();
+ }
+}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/ClassFreeUberspect.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyServiceOps.java
similarity index 55%
copy from core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/ClassFreeUberspect.java
copy to core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyServiceOps.java
index aec38b8..ce88ef3 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/ClassFreeUberspect.java
+++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/DummyServiceOps.java
@@ -16,26 +16,33 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.provisioning.java.jexl;
+package org.apache.syncope.core.logic;
-import org.apache.commons.jexl3.internal.introspection.Uberspect;
-import org.apache.commons.jexl3.introspection.JexlMethod;
-import org.apache.commons.jexl3.introspection.JexlPropertyGet;
+import java.util.List;
+import org.apache.syncope.common.keymaster.client.api.ServiceOps;
+import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
+import org.springframework.stereotype.Component;
-class ClassFreeUberspect extends Uberspect {
+@Component
+public class DummyServiceOps implements ServiceOps {
- ClassFreeUberspect() {
- super(null, null);
+ @Override
+ public void register(final NetworkService service) {
+ // do nothing
}
@Override
- public JexlPropertyGet getPropertyGet(final Object obj, final Object identifier) {
- return "class".equals(identifier) ? null : super.getPropertyGet(obj, identifier);
+ public void unregister(final NetworkService service) {
+ // do nothing
}
@Override
- public JexlMethod getMethod(final Object obj, final String method, final Object... args) {
- return "getClass".equals(method) ? null : super.getMethod(obj, method, args);
+ public List<NetworkService> list(final NetworkService.Type serviceType) {
+ return List.of();
}
+ @Override
+ public NetworkService get(final NetworkService.Type serviceType) {
+ return null;
+ }
}
diff --git a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/IdMLogicTestContext.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/IdMLogicTestContext.java
new file mode 100644
index 0000000..2b4d0f9
--- /dev/null
+++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/IdMLogicTestContext.java
@@ -0,0 +1,56 @@
+/*
+ * 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.logic;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import org.apache.syncope.core.persistence.api.ImplementationLookup;
+import org.apache.syncope.core.persistence.jpa.PersistenceContext;
+import org.apache.syncope.core.provisioning.java.ProvisioningContext;
+import org.apache.syncope.core.spring.security.SecurityContext;
+import org.apache.syncope.core.workflow.java.WorkflowContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.Primary;
+import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
+
+@Import({ SecurityContext.class, PersistenceContext.class, ProvisioningContext.class, WorkflowContext.class })
+@ComponentScan("org.apache.syncope.core.logic")
+@Configuration
+public class IdMLogicTestContext {
+
+ @Bean
+ public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
+ PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer();
+ pspc.setIgnoreResourceNotFound(true);
+ pspc.setIgnoreUnresolvablePlaceholders(true);
+ return pspc;
+ }
+
+ @Primary
+ @Bean
+ public ImplementationLookup classPathScanImplementationLookup()
+ throws ClassNotFoundException, InstantiationException, IllegalAccessException,
+ NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
+
+ return new DummyImplementationLookup();
+ }
+}
diff --git a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ReconciliationLogicTest.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ReconciliationLogicTest.java
new file mode 100644
index 0000000..72d1a93
--- /dev/null
+++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ReconciliationLogicTest.java
@@ -0,0 +1,153 @@
+/*
+ * 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.logic;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import com.fasterxml.jackson.databind.MappingIterator;
+import com.fasterxml.jackson.dataformat.csv.CsvMapper;
+import com.fasterxml.jackson.dataformat.csv.CsvSchema;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.common.rest.api.beans.CSVPullSpec;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
+import org.apache.syncope.common.rest.api.beans.CSVPushSpec;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+@Transactional("Master")
+public class ReconciliationLogicTest extends AbstractTest {
+
+ @Autowired
+ private ReconciliationLogic reconciliationLogic;
+
+ @Autowired
+ private UserLogic userLogic;
+
+ @Test
+ public void pullFromCSV() {
+ CSVPullSpec spec = new CSVPullSpec.Builder(AnyTypeKind.USER.name(), "username").build();
+ InputStream csv = getClass().getResourceAsStream("/test1.csv");
+
+ List<ProvisioningReport> results = AuthContextUtils.callAsAdmin(SyncopeConstants.MASTER_DOMAIN, () -> {
+ return reconciliationLogic.pull(spec, csv);
+ });
+ assertEquals(2, results.size());
+
+ assertEquals(AnyTypeKind.USER.name(), results.get(0).getAnyType());
+ assertNotNull(results.get(0).getKey());
+ assertEquals("donizetti", results.get(0).getName());
+ assertEquals("donizetti", results.get(0).getUidValue());
+ assertEquals(ResourceOperation.CREATE, results.get(0).getOperation());
+ assertEquals(ProvisioningReport.Status.SUCCESS, results.get(0).getStatus());
+
+ AuthContextUtils.callAsAdmin(SyncopeConstants.MASTER_DOMAIN, () -> {
+ UserTO donizetti = userLogic.read(results.get(0).getKey());
+ assertNotNull(donizetti);
+ assertEquals("Gaetano", donizetti.getPlainAttr("firstname").get().getValues().get(0));
+ assertEquals(1, donizetti.getPlainAttr("loginDate").get().getValues().size());
+
+ UserTO cimarosa = userLogic.read(results.get(1).getKey());
+ assertNotNull(cimarosa);
+ assertEquals("Domenico Cimarosa", cimarosa.getPlainAttr("fullname").get().getValues().get(0));
+ assertEquals(2, cimarosa.getPlainAttr("loginDate").get().getValues().size());
+
+ return null;
+ });
+ }
+
+ @Test
+ public void pushToCSV() throws IOException {
+ Pair<Integer, List<UserTO>> search = AuthContextUtils.callAsAdmin(SyncopeConstants.MASTER_DOMAIN,
+ () -> userLogic.search(null, 1, 100, List.of(), SyncopeConstants.ROOT_REALM, false));
+ assertNotNull(search);
+
+ CSVPushSpec spec = new CSVPushSpec.Builder(AnyTypeKind.USER.name()).
+ ignorePagination(true).
+ field("username").
+ field("status").
+ plainAttr("firstname").
+ plainAttr("surname").
+ plainAttr("email").
+ plainAttr("loginDate").
+ build();
+
+ PipedInputStream in = new PipedInputStream();
+ PipedOutputStream os = new PipedOutputStream(in);
+
+ List<ProvisioningReport> results = AuthContextUtils.callAsAdmin(SyncopeConstants.MASTER_DOMAIN, () -> {
+ return reconciliationLogic.push(null, 1, 1, List.of(), SyncopeConstants.ROOT_REALM, spec, os);
+ });
+ assertEquals(search.getLeft(), results.size());
+
+ CsvSchema.Builder builder = CsvSchema.builder().setUseHeader(true);
+ builder.addColumn("username");
+ builder.addColumn("status");
+ builder.addColumn("firstname");
+ builder.addColumn("surname");
+ builder.addColumn("email");
+ builder.addColumn("loginDate");
+ CsvSchema schema = builder.build();
+
+ MappingIterator<Map<String, String>> reader = new CsvMapper().readerFor(Map.class).with(schema).readValues(in);
+
+ for (int i = 0; i < results.size() && reader.hasNext(); i++) {
+ Map<String, String> row = reader.next();
+
+ assertEquals(results.get(i).getName(), row.get("username"));
+ assertEquals(search.getRight().stream().filter(user -> row.get("username").equals(user.getUsername())).
+ findFirst().get().getStatus(),
+ row.get("status"));
+
+ switch (row.get("username")) {
+ case "rossini":
+ assertEquals(spec.getNullValue(), row.get("email"));
+ assertTrue(row.get("loginDate").contains(spec.getArrayElementSeparator()));
+ break;
+
+ case "verdi":
+ assertEquals("verdi@syncope.org", row.get("email"));
+ assertEquals(spec.getNullValue(), row.get("loginDate"));
+ break;
+
+ case "bellini":
+ assertEquals(spec.getNullValue(), row.get("email"));
+ assertFalse(row.get("loginDate").contains(spec.getArrayElementSeparator()));
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/TestInitializer.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/TestInitializer.java
new file mode 100644
index 0000000..1a4a652
--- /dev/null
+++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/TestInitializer.java
@@ -0,0 +1,70 @@
+/*
+ * 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.logic;
+
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.core.persistence.api.DomainHolder;
+import org.apache.syncope.core.persistence.api.content.ContentLoader;
+import org.apache.syncope.core.persistence.jpa.StartupDomainLoader;
+import org.apache.syncope.core.spring.ApplicationContextProvider;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.support.TransactionSynchronizationManager;
+
+@Component
+public class TestInitializer implements InitializingBean, ApplicationContextAware {
+
+ private ConfigurableApplicationContext ctx;
+
+ @Autowired
+ private StartupDomainLoader domainLoader;
+
+ @Autowired
+ private DomainHolder domainHolder;
+
+ @Autowired
+ private ContentLoader contentLoader;
+
+ @Override
+ public void setApplicationContext(final ApplicationContext ctx) throws BeansException {
+ this.ctx = (ConfigurableApplicationContext) ctx;
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ ApplicationContextProvider.setApplicationContext(ctx);
+ ApplicationContextProvider.setBeanFactory((DefaultListableBeanFactory) ctx.getBeanFactory());
+
+ if (!TransactionSynchronizationManager.isSynchronizationActive()) {
+ TransactionSynchronizationManager.initSynchronization();
+ }
+
+ domainLoader.load();
+
+ contentLoader.load(
+ SyncopeConstants.MASTER_DOMAIN,
+ domainHolder.getDomains().get(SyncopeConstants.MASTER_DOMAIN));
+ }
+}
diff --git a/core/idm/logic/src/test/resources/logicTest.xml b/core/idm/logic/src/test/resources/logicTest.xml
new file mode 100644
index 0000000..af40284
--- /dev/null
+++ b/core/idm/logic/src/test/resources/logicTest.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd">
+
+ <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
+ <property name="locations">
+ <list>
+ <value>classpath:persistence.properties</value>
+ <value>classpath:domains/*.properties</value>
+ <value>classpath:security.properties</value>
+ <value>classpath:connid.properties</value>
+ <value>classpath:mail.properties</value>
+ <value>classpath:workflow.properties</value>
+ <value>classpath:provisioning.properties</value>
+ <value>classpath:logic.properties</value>
+ </list>
+ </property>
+ <property name="ignoreResourceNotFound" value="true"/>
+ <property name="ignoreUnresolvablePlaceholders" value="true"/>
+ </bean>
+
+ <bean id="jwtIssuer" class="java.lang.String">
+ <constructor-arg value="${jwtIssuer}"/>
+ </bean>
+ <bean id="jwsKey" class="java.lang.String">
+ <constructor-arg value="ZW7pRixehFuNUtnY5Se47IemgMryTzazPPJ9CGX5LTCmsOJpOgHAQEuPQeV9A28f"/>
+ </bean>
+ <bean id="accessTokenJwsSignatureVerifier"
+ class="org.apache.syncope.core.spring.security.jws.AccessTokenJwsSignatureVerifier">
+ <property name="jwsAlgorithm" value="${jwsAlgorithm}"/>
+ <property name="jwsKey" value="ZW7pRixehFuNUtnY5Se47IemgMryTzazPPJ9CGX5LTCmsOJpOgHAQEuPQeV9A28f"/>
+ </bean>
+ <bean id="accessTokenJwsSignatureProvider"
+ class="org.apache.syncope.core.spring.security.jws.AccessTokenJwsSignatureProvider">
+ <property name="jwsAlgorithm" value="${jwsAlgorithm}"/>
+ <property name="jwsKey" value="ZW7pRixehFuNUtnY5Se47IemgMryTzazPPJ9CGX5LTCmsOJpOgHAQEuPQeV9A28f"/>
+ </bean>
+ <bean id="credentialChecker" class="org.apache.syncope.core.spring.security.DefaultCredentialChecker">
+ <constructor-arg value="ZW7pRixehFuNUtnY5Se47IemgMryTzazPPJ9CGX5LTCmsOJpOgHAQEuPQeV9A28f" index="0"/>
+ <constructor-arg value="DE088591C00CC98B36F5ADAAF7DA2B004CF7F2FE7BBB45B766B6409876E2F3DB13C7905C6AA59464" index="1"/>
+ <constructor-arg value="anonymousKey" index="2"/>
+ </bean>
+
+ <import resource="logicContext.xml"/>
+</beans>
diff --git a/core/idm/logic/src/test/resources/test1.csv b/core/idm/logic/src/test/resources/test1.csv
new file mode 100644
index 0000000..0ea7355
--- /dev/null
+++ b/core/idm/logic/src/test/resources/test1.csv
@@ -0,0 +1,3 @@
+"username","email","surname","firstname","fullname","userId","loginDate"
+"donizetti","donizetti@apache.org","Donizetti","Gaetano","Gaetano Donizetti","donizetti@apache.org","2019-12-24"
+"cimarosa","cimarosa@apache.org","Cimarosa","Domenico","Domenico Cimarosa","cimarosa@apache.org","2018-11-21;2018-12-24"
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 7b77c2f..6f3b1a1 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
@@ -18,13 +18,27 @@
*/
package org.apache.syncope.core.rest.cxf.service;
+import java.io.InputStream;
+import java.util.List;
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.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.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.beans.AnyQuery;
+import org.apache.syncope.common.rest.api.beans.CSVPullSpec;
+import org.apache.syncope.common.rest.api.beans.CSVPushSpec;
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.spring.security.AuthContextUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -59,4 +73,33 @@ public class ReconciliationServiceImpl extends AbstractServiceImpl implements Re
validate(reconQuery);
logic.pull(reconQuery, pullTask);
}
+
+ @Override
+ public Response push(final AnyQuery anyQuery, final CSVPushSpec spec) {
+ String realm = StringUtils.prependIfMissing(anyQuery.getRealm(), SyncopeConstants.ROOT_REALM);
+
+ SearchCond searchCond = StringUtils.isBlank(anyQuery.getFiql())
+ ? null
+ : getSearchCond(anyQuery.getFiql(), realm);
+
+ StreamingOutput sout = (os) -> logic.push(
+ searchCond,
+ anyQuery.getPage(),
+ anyQuery.getSize(),
+ getOrderByClauses(anyQuery.getOrderBy()),
+ realm,
+ spec,
+ os);
+
+ return Response.ok(sout).
+ type(RESTHeaders.TEXT_CSV).
+ header(HttpHeaders.CONTENT_DISPOSITION,
+ "attachment; filename=" + AuthContextUtils.getDomain() + ".csv").
+ build();
+ }
+
+ @Override
+ public List<ProvisioningReport> pull(final CSVPullSpec spec, final InputStream csv) {
+ return logic.pull(spec, csv);
+ }
}
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 beb377a..c572d98 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
@@ -19,7 +19,6 @@
package org.apache.syncope.core.rest.cxf.service;
import java.net.URI;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -104,7 +103,7 @@ public class ResourceServiceImpl extends AbstractServiceImpl implements Resource
final String key, final String anyTypeKey, final ConnObjectTOQuery query) {
Filter filter = null;
- Set<String> moreAttrsToGet = Collections.emptySet();
+ Set<String> moreAttrsToGet = Set.of();
if (StringUtils.isNotBlank(query.getFiql())) {
try {
FilterVisitor visitor = new FilterVisitor();
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
index 05efdb7..d0ac105 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
@@ -86,16 +86,17 @@ public class AnyObjectLogic extends AbstractAnyLogic<AnyObjectTO, AnyObjectCR, A
throw new UnsupportedOperationException("Need to specify " + AnyType.class.getSimpleName());
}
- Set<String> effectiveRealms = RealmUtils.getEffective(
+ Set<String> adminRealms = RealmUtils.getEffective(
AuthContextUtils.getAuthorizations().get(AnyEntitlement.SEARCH.getFor(searchCond.hasAnyTypeCond())),
realm);
- int count = searchDAO.count(effectiveRealms, searchCond, AnyTypeKind.ANY_OBJECT);
+ int count = searchDAO.count(adminRealms, searchCond, AnyTypeKind.ANY_OBJECT);
List<AnyObject> matching = searchDAO.search(
- effectiveRealms, searchCond, page, size, orderBy, AnyTypeKind.ANY_OBJECT);
+ adminRealms, searchCond, page, size, orderBy, AnyTypeKind.ANY_OBJECT);
List<AnyObjectTO> result = matching.stream().
- map(anyObject -> binder.getAnyObjectTO(anyObject, details)).collect(Collectors.toList());
+ map(anyObject -> binder.getAnyObjectTO(anyObject, details)).
+ collect(Collectors.toList());
return Pair.of(count, result);
}
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
index f563347..daf795b 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
@@ -24,7 +24,6 @@ import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Resource;
@@ -33,7 +32,6 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.request.GroupCR;
import org.apache.syncope.common.lib.request.GroupUR;
import org.apache.syncope.common.lib.request.StringPatchItem;
@@ -159,16 +157,18 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupCR, GroupUR> {
final String realm,
final boolean details) {
- int count = searchDAO.count(
- RealmUtils.getEffective(SyncopeConstants.FULL_ADMIN_REALMS, realm),
- Optional.ofNullable(searchCond).orElseGet(() -> groupDAO.getAllMatchingCond()), AnyTypeKind.GROUP);
+ Set<String> adminRealms = RealmUtils.getEffective(
+ AuthContextUtils.getAuthorizations().get(IdRepoEntitlement.GROUP_SEARCH), realm);
+
+ SearchCond effectiveCond = searchCond == null ? groupDAO.getAllMatchingCond() : searchCond;
+
+ int count = searchDAO.count(adminRealms, effectiveCond, AnyTypeKind.GROUP);
List<Group> matching = searchDAO.search(
- RealmUtils.getEffective(SyncopeConstants.FULL_ADMIN_REALMS, realm),
- Optional.ofNullable(searchCond).orElseGet(() -> groupDAO.getAllMatchingCond()),
- page, size, orderBy, AnyTypeKind.GROUP);
+ adminRealms, effectiveCond, page, size, orderBy, AnyTypeKind.GROUP);
List<GroupTO> result = matching.stream().
- map(group -> binder.getGroupTO(group, details)).collect(Collectors.toList());
+ map(group -> binder.getGroupTO(group, details)).
+ collect(Collectors.toList());
return Pair.of(count, result);
}
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
index 16b0122..41acb64 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/TaskLogic.java
@@ -43,11 +43,14 @@ import org.apache.syncope.common.lib.types.IdRepoEntitlement;
import org.apache.syncope.common.lib.types.TaskType;
import org.apache.syncope.common.rest.api.RESTHeaders;
import org.apache.syncope.common.rest.api.batch.BatchResponseItem;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.dao.NotificationDAO;
import org.apache.syncope.core.persistence.api.dao.NotFoundException;
import org.apache.syncope.core.persistence.api.dao.TaskDAO;
import org.apache.syncope.core.persistence.api.dao.TaskExecDAO;
import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
import org.apache.syncope.core.persistence.api.entity.task.NotificationTask;
+import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
import org.apache.syncope.core.persistence.api.entity.task.Task;
import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
@@ -56,8 +59,6 @@ import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory;
import org.apache.syncope.core.provisioning.api.data.TaskDataBinder;
import org.apache.syncope.core.provisioning.api.job.JobNamer;
import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
-import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
-import org.apache.syncope.core.persistence.api.dao.NotificationDAO;
import org.apache.syncope.core.provisioning.api.notification.NotificationJobDelegate;
import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
import org.apache.syncope.core.provisioning.api.utils.ExceptionUtils2;
@@ -232,18 +233,17 @@ public class TaskLogic extends AbstractExecutableLogic<TaskTO> {
TaskUtils taskUtil = taskUtilsFactory.getInstance(task);
String executor = AuthContextUtils.getUsername();
-
+
ExecTO result = null;
switch (taskUtil.getType()) {
case PROPAGATION:
PropagationTaskTO taskTO = binder.<PropagationTaskTO>getTaskTO(task, taskUtil, false);
- PropagationTaskInfo taskInfo = new PropagationTaskInfo();
+ PropagationTaskInfo taskInfo = new PropagationTaskInfo(((PropagationTask) task).getResource());
taskInfo.setKey(taskTO.getKey());
taskInfo.setOperation(taskTO.getOperation());
taskInfo.setConnObjectKey(taskTO.getConnObjectKey());
taskInfo.setOldConnObjectKey(taskTO.getOldConnObjectKey());
taskInfo.setAttributes(taskTO.getAttributes());
- taskInfo.setResource(taskTO.getResource());
taskInfo.setObjectClassName(taskTO.getObjectClassName());
taskInfo.setAnyTypeKind(taskTO.getAnyTypeKind());
taskInfo.setAnyType(taskTO.getAnyType());
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
index 0651a98..270c438 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
@@ -23,7 +23,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
-import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.ArrayUtils;
@@ -111,14 +110,14 @@ public class UserLogic extends AbstractAnyLogic<UserTO, UserCR, UserUR> {
final String realm,
final boolean details) {
- int count = searchDAO.count(RealmUtils.getEffective(
- AuthContextUtils.getAuthorizations().get(IdRepoEntitlement.USER_SEARCH), realm),
- Optional.ofNullable(searchCond).orElseGet(() -> userDAO.getAllMatchingCond()), AnyTypeKind.USER);
+ Set<String> adminRealms = RealmUtils.getEffective(
+ AuthContextUtils.getAuthorizations().get(IdRepoEntitlement.USER_SEARCH), realm);
- List<User> matching = searchDAO.search(RealmUtils.getEffective(
- AuthContextUtils.getAuthorizations().get(IdRepoEntitlement.USER_SEARCH), realm),
- Optional.ofNullable(searchCond).orElseGet(() -> userDAO.getAllMatchingCond()),
- page, size, orderBy, AnyTypeKind.USER);
+ SearchCond effectiveCond = searchCond == null ? userDAO.getAllMatchingCond() : searchCond;
+
+ int count = searchDAO.count(adminRealms, effectiveCond, AnyTypeKind.USER);
+
+ List<User> matching = searchDAO.search(adminRealms, effectiveCond, page, size, orderBy, AnyTypeKind.USER);
List<UserTO> result = matching.stream().
map(user -> binder.returnUserTO(binder.getUserTO(user, details))).
collect(Collectors.toList());
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONPlainSchemaDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONPlainSchemaDAO.java
index 07c9969..81bce44 100644
--- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONPlainSchemaDAO.java
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONPlainSchemaDAO.java
@@ -30,6 +30,6 @@ public class MyJPAJSONPlainSchemaDAO extends AbstractJPAJSONPlainSchemaDAO {
"SELECT COUNT(id) FROM " + new SearchSupport(getAnyTypeKind(reference)).field().name
+ " WHERE JSON_CONTAINS(plainAttrs, '[{\"schema\":\"" + schema.getKey() + "\"}]')");
- return (long) query.getSingleResult() > 0;
+ return ((Number) query.getSingleResult()).intValue() > 0;
}
}
diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONPlainSchemaDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONPlainSchemaDAO.java
index 77019a7..c4d27d6 100644
--- a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONPlainSchemaDAO.java
+++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONPlainSchemaDAO.java
@@ -30,6 +30,6 @@ public class PGJPAJSONPlainSchemaDAO extends AbstractJPAJSONPlainSchemaDAO {
"SELECT COUNT(id) FROM " + new SearchSupport(getAnyTypeKind(reference)).field().name
+ " WHERE plainAttrs @> '[{\"schema\":\"" + schema.getKey() + "\"}]'::jsonb");
- return (long) query.getSingleResult() > 0;
+ return ((Number) query.getSingleResult()).intValue() > 0;
}
}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
index 05d6a34..0be1f96 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractAnyDAO.java
@@ -485,22 +485,18 @@ public abstract class AbstractAnyDAO<A extends Any<?>> extends AbstractDAO<A> im
// schemas given by type extensions
Map<Group, List<? extends AnyTypeClass>> typeExtensionClasses = new HashMap<>();
if (any instanceof User) {
- ((User) any).getMemberships().forEach(
- memb -> memb.getRightEnd().getTypeExtensions().forEach(
- typeExtension -> typeExtensionClasses.put(
- memb.getRightEnd(), typeExtension.getAuxClasses())));
+ ((User) any).getMemberships().forEach(memb -> memb.getRightEnd().getTypeExtensions().
+ forEach(typeExt -> typeExtensionClasses.put(memb.getRightEnd(), typeExt.getAuxClasses())));
} else if (any instanceof AnyObject) {
- ((AnyObject) any).getMemberships().
- forEach(memb -> memb.getRightEnd().getTypeExtensions().stream().
- filter(typeExtension -> any.getType().equals(typeExtension.getAnyType())).
- forEach(typeExtension -> typeExtensionClasses.put(
- memb.getRightEnd(), typeExtension.getAuxClasses())));
+ ((AnyObject) any).getMemberships().forEach(memb -> memb.getRightEnd().getTypeExtensions().stream().
+ filter(typeExt -> any.getType().equals(typeExt.getAnyType())).
+ forEach(typeExt -> typeExtensionClasses.put(memb.getRightEnd(), typeExt.getAuxClasses())));
}
typeExtensionClasses.entrySet().stream().map(entry -> {
result.getForMemberships().put(entry.getKey(), new HashSet<>());
return entry;
- }).forEachOrdered((entry) -> entry.getValue().forEach(typeClass -> {
+ }).forEach(entry -> entry.getValue().forEach(typeClass -> {
if (reference.equals(PlainSchema.class)) {
result.getForMemberships().get(entry.getKey()).
addAll((Collection<? extends S>) typeClass.getPlainSchemas());
@@ -543,7 +539,7 @@ public abstract class AbstractAnyDAO<A extends Any<?>> extends AbstractDAO<A> im
query.getResultList().stream().map(resultKey -> resultKey instanceof Object[]
? (String) ((Object[]) resultKey)[0]
: ((String) resultKey)).
- forEachOrdered((actualKey) -> {
+ forEach((actualKey) -> {
DynRealm dynRealm = dynRealmDAO.find(actualKey.toString());
if (dynRealm == null) {
LOG.error("Could not find dynRealm with id {}, even though returned by the native query",
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPlainSchemaDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPlainSchemaDAO.java
index 46f98ad..9309935 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPlainSchemaDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPlainSchemaDAO.java
@@ -27,7 +27,6 @@ import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
import org.apache.syncope.core.persistence.api.dao.PlainAttrDAO;
import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
-import org.apache.syncope.core.persistence.api.entity.AnyUtils;
import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
import org.apache.syncope.core.persistence.api.entity.Implementation;
import org.apache.syncope.core.persistence.api.entity.PlainAttr;
@@ -118,7 +117,7 @@ public class JPAPlainSchemaDAO extends AbstractDAO<PlainSchema> implements Plain
+ ".schema_id WHERE " + JPAPlainSchema.TABLE + ".id = ?1");
query.setParameter(1, schema.getKey());
- return (long) query.getSingleResult() > 0;
+ return ((Number) query.getSingleResult()).intValue() > 0;
}
@Override
@@ -128,9 +127,8 @@ public class JPAPlainSchemaDAO extends AbstractDAO<PlainSchema> implements Plain
protected void deleteAttrs(final PlainSchema schema) {
for (AnyTypeKind anyTypeKind : AnyTypeKind.values()) {
- AnyUtils anyUtils = anyUtilsFactory.getInstance(anyTypeKind);
-
- findAttrs(schema, anyUtils.plainAttrClass()).forEach(attr -> plainAttrDAO.delete(attr));
+ findAttrs(schema, anyUtilsFactory.getInstance(anyTypeKind).plainAttrClass()).
+ forEach(attr -> plainAttrDAO.delete(attr));
}
}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResource.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResource.java
index 020d4a5..e61568e 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResource.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResource.java
@@ -124,16 +124,16 @@ public class JPAExternalResource extends AbstractProvidedKeyEntity implements Ex
@NotNull
private TraceLevel provisioningTraceLevel = TraceLevel.FAILURES;
- @ManyToOne(fetch = FetchType.LAZY)
+ @ManyToOne(fetch = FetchType.EAGER)
private JPAPasswordPolicy passwordPolicy;
- @ManyToOne(fetch = FetchType.LAZY)
+ @ManyToOne(fetch = FetchType.EAGER)
private JPAAccountPolicy accountPolicy;
- @ManyToOne(fetch = FetchType.LAZY)
+ @ManyToOne(fetch = FetchType.EAGER)
private JPAPullPolicy pullPolicy;
- @ManyToOne(fetch = FetchType.LAZY)
+ @ManyToOne(fetch = FetchType.EAGER)
private JPAPushPolicy pushPolicy;
/**
diff --git a/core/provisioning-api/pom.xml b/core/provisioning-api/pom.xml
index 103db94..f803af7 100644
--- a/core/provisioning-api/pom.xml
+++ b/core/provisioning-api/pom.xml
@@ -39,6 +39,11 @@ under the License.
<dependencies>
<dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-jexl3</artifactId>
+ </dependency>
+
+ <dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
@@ -50,12 +55,16 @@ under the License.
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-afterburner</artifactId>
</dependency>
-
+
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
-
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-tx</artifactId>
+ </dependency>
+
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
@@ -66,6 +75,22 @@ under the License.
<artifactId>syncope-core-persistence-api</artifactId>
<version>${project.version}</version>
</dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-junit-jupiter</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/Connector.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/Connector.java
index 09de13c..1c11784 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/Connector.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/Connector.java
@@ -21,6 +21,7 @@ package org.apache.syncope.core.provisioning.api;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
import org.apache.syncope.core.persistence.api.entity.ConnInstance;
import org.apache.syncope.core.provisioning.api.pushpull.ReconFilterBuilder;
@@ -29,11 +30,15 @@ import org.identityconnectors.framework.common.objects.ConnectorObject;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.identityconnectors.framework.common.objects.ObjectClassInfo;
import org.identityconnectors.framework.common.objects.OperationOptions;
+import org.identityconnectors.framework.common.objects.OperationOptionsBuilder;
import org.identityconnectors.framework.common.objects.SyncResultsHandler;
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.common.objects.SearchResult;
+import org.identityconnectors.framework.common.objects.SortKey;
+import org.identityconnectors.framework.common.objects.SyncDeltaBuilder;
+import org.identityconnectors.framework.common.objects.SyncDeltaType;
import org.identityconnectors.framework.spi.SearchResultsHandler;
/**
@@ -104,7 +109,9 @@ public interface Connector {
* @param handler to be used to handle deltas.
* @param options ConnId's OperationOptions.
*/
- void fullReconciliation(ObjectClass objectClass, SyncResultsHandler handler, OperationOptions options);
+ default void fullReconciliation(ObjectClass objectClass, SyncResultsHandler handler, OperationOptions options) {
+ filteredReconciliation(objectClass, null, handler, options);
+ }
/**
* Fetches remote objects (for use during filtered reconciliation).
@@ -114,11 +121,36 @@ public interface Connector {
* @param handler to be used to handle deltas.
* @param options ConnId's OperationOptions.
*/
- void filteredReconciliation(
+ default void filteredReconciliation(
ObjectClass objectClass,
ReconFilterBuilder filterBuilder,
SyncResultsHandler handler,
- OperationOptions options);
+ OperationOptions options) {
+
+ Filter filter = null;
+ OperationOptions actualOptions = options;
+ if (filterBuilder != null) {
+ filter = filterBuilder.build();
+ actualOptions = filterBuilder.build(actualOptions);
+ }
+
+ search(objectClass, filter, new SearchResultsHandler() {
+
+ @Override
+ public void handleResult(final SearchResult result) {
+ // nothing to do
+ }
+
+ @Override
+ public boolean handle(final ConnectorObject object) {
+ return handler.handle(new SyncDeltaBuilder().
+ setObject(object).
+ setDeltaType(SyncDeltaType.CREATE_OR_UPDATE).
+ setToken(new SyncToken("")).
+ build());
+ }
+ }, actualOptions);
+ }
/**
* Sync remote objects from a connector instance.
@@ -182,14 +214,27 @@ public interface Connector {
* @param options ConnId's OperationOptions
* @return search result
*/
- SearchResult search(
+ default SearchResult search(
ObjectClass objectClass,
Filter filter,
SearchResultsHandler handler,
int pageSize,
String pagedResultsCookie,
List<OrderByClause> orderBy,
- OperationOptions options);
+ OperationOptions options) {
+
+ OperationOptionsBuilder builder = new OperationOptionsBuilder().setPageSize(pageSize).setPagedResultsOffset(-1);
+ if (pagedResultsCookie != null) {
+ builder.setPagedResultsCookie(pagedResultsCookie);
+ }
+ builder.setSortKeys(orderBy.stream().
+ map(clause -> new SortKey(clause.getField(), clause.getDirection() == OrderByClause.Direction.ASC)).
+ collect(Collectors.toList()));
+
+ builder.setAttributesToGet(options.getAttributesToGet());
+
+ return search(objectClass, filter, handler, builder.build());
+ }
/**
* Builds metadata description of ConnId {@link ObjectClass}.
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/IntAttrNameParser.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/IntAttrNameParser.java
similarity index 98%
rename from core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/IntAttrNameParser.java
rename to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/IntAttrNameParser.java
index ab1dc4d..fad50f4 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/IntAttrNameParser.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/IntAttrNameParser.java
@@ -16,10 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.provisioning.java;
+package org.apache.syncope.core.provisioning.api;
import java.text.ParseException;
-import org.apache.syncope.core.provisioning.api.IntAttrName;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.tuple.Pair;
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/UserProvisioningManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/UserProvisioningManager.java
index b397149..4ddccca 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/UserProvisioningManager.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/UserProvisioningManager.java
@@ -26,8 +26,8 @@ import org.apache.syncope.common.lib.request.StatusR;
import org.apache.syncope.common.lib.request.UserCR;
import org.apache.syncope.common.lib.request.UserUR;
import org.apache.syncope.common.lib.to.PropagationStatus;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
import org.apache.syncope.common.lib.to.UserTO;
-import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
public interface UserProvisioningManager extends ProvisioningManager<UserTO, UserCR, UserUR> {
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/ClassFreeUberspect.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/ClassFreeUberspect.java
similarity index 96%
rename from core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/ClassFreeUberspect.java
rename to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/ClassFreeUberspect.java
index aec38b8..e218836 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/ClassFreeUberspect.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/ClassFreeUberspect.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.provisioning.java.jexl;
+package org.apache.syncope.core.provisioning.api.jexl;
import org.apache.commons.jexl3.internal.introspection.Uberspect;
import org.apache.commons.jexl3.introspection.JexlMethod;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/EmptyClassLoader.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/EmptyClassLoader.java
similarity index 96%
rename from core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/EmptyClassLoader.java
rename to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/EmptyClassLoader.java
index 037113e..2457d7f 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/EmptyClassLoader.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/EmptyClassLoader.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.provisioning.java.jexl;
+package org.apache.syncope.core.provisioning.api.jexl;
/**
* A class loader that will throw {@link ClassNotFoundException} for every class name.
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/JexlUtils.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtils.java
similarity index 94%
rename from core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/JexlUtils.java
rename to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtils.java
index 83ee4cf..ba0e63f 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/JexlUtils.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtils.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.provisioning.java.jexl;
+package org.apache.syncope.core.provisioning.api.jexl;
import java.beans.IntrospectionException;
import java.beans.Introspector;
@@ -44,7 +44,6 @@ import org.apache.commons.lang3.tuple.Pair;
import org.apache.syncope.common.lib.to.AnyTO;
import org.apache.syncope.common.lib.Attr;
import org.apache.syncope.common.lib.to.RealmTO;
-import org.apache.syncope.core.spring.ApplicationContextProvider;
import org.apache.syncope.core.provisioning.api.utils.FormatUtils;
import org.apache.syncope.core.persistence.api.entity.Any;
import org.apache.syncope.core.persistence.api.entity.DerSchema;
@@ -227,17 +226,24 @@ public final class JexlUtils {
});
}
- public static void addDerAttrsToContext(final Any<?> any, final JexlContext jexlContext) {
- Map<DerSchema, String> derAttrs =
- ApplicationContextProvider.getBeanFactory().getBean(DerAttrHandler.class).getValues(any);
+ public static void addDerAttrsToContext(
+ final Any<?> any,
+ final DerAttrHandler derAttrHandler,
+ final JexlContext jexlContext) {
+
+ Map<DerSchema, String> derAttrs = derAttrHandler.getValues(any);
derAttrs.forEach((schema, value) -> jexlContext.set(schema.getKey(), value));
}
- public static boolean evaluateMandatoryCondition(final String mandatoryCondition, final Any<?> any) {
+ public static boolean evaluateMandatoryCondition(
+ final String mandatoryCondition,
+ final Any<?> any,
+ final DerAttrHandler derAttrHandler) {
+
JexlContext jexlContext = new MapContext();
addPlainAttrsToContext(any.getPlainAttrs(), jexlContext);
- addDerAttrsToContext(any, jexlContext);
+ addDerAttrsToContext(any, derAttrHandler, jexlContext);
return Boolean.parseBoolean(evaluate(mandatoryCondition, jexlContext));
}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/SyncopeJexlFunctions.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/SyncopeJexlFunctions.java
similarity index 97%
rename from core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/SyncopeJexlFunctions.java
rename to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/SyncopeJexlFunctions.java
index 1dd92a8..9cc011f 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/SyncopeJexlFunctions.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/SyncopeJexlFunctions.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.provisioning.java.jexl;
+package org.apache.syncope.core.provisioning.api.jexl;
import java.util.Arrays;
import java.util.Collections;
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationManager.java
index 523cc22..048f06b 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationManager.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationManager.java
@@ -20,13 +20,22 @@ package org.apache.syncope.core.provisioning.api.propagation;
import java.util.Collection;
import java.util.List;
+import java.util.Set;
+import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.syncope.common.lib.request.UserUR;
import org.apache.syncope.common.lib.Attr;
import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.core.persistence.api.entity.Any;
import org.apache.syncope.core.provisioning.api.PropagationByResource;
import org.apache.syncope.core.persistence.api.entity.Realm;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.resource.Item;
+import org.apache.syncope.core.persistence.api.entity.resource.Provision;
+import org.apache.syncope.core.provisioning.api.DerAttrHandler;
import org.apache.syncope.core.provisioning.api.UserWorkflowResult;
+import org.identityconnectors.framework.common.objects.Attribute;
public interface PropagationManager {
@@ -147,6 +156,16 @@ public interface PropagationManager {
PropagationByResource<Pair<String, String>> propByLinkedAccount,
Collection<String> noPropResourceKeys);
+ PropagationTaskInfo newTask(
+ DerAttrHandler derAttrHandler,
+ Any<?> any,
+ ExternalResource resource,
+ ResourceOperation operation,
+ Provision provision,
+ boolean deleteOnResource,
+ Stream<? extends Item> mappingItems,
+ Pair<String, Set<Attribute>> preparedAttrs);
+
/**
* Create the needed tasks for the realm for each resource associated, unless in {@code noPropResourceKeys}.
*
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskExecutor.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskExecutor.java
index 6447fca..50455b6 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskExecutor.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskExecutor.java
@@ -70,6 +70,6 @@ public interface PropagationTaskExecutor {
* @param executor the executor of this task
* @return reporter to report propagation execution status
*/
- PropagationReporter execute(Collection<PropagationTaskInfo> taskInfos, boolean nullPriorityAsync,
- String executor);
+ PropagationReporter execute(
+ Collection<PropagationTaskInfo> taskInfos, boolean nullPriorityAsync, String executor);
}
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskInfo.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskInfo.java
index 322a163..c3b3e44 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskInfo.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/propagation/PropagationTaskInfo.java
@@ -19,22 +19,57 @@
package org.apache.syncope.core.provisioning.api.propagation;
import java.util.Optional;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.syncope.common.lib.to.PropagationTaskTO;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.provisioning.api.Connector;
import org.identityconnectors.framework.common.objects.ConnectorObject;
public class PropagationTaskInfo extends PropagationTaskTO {
private static final long serialVersionUID = -2879861567335503099L;
+ private final ExternalResource externalResource;
+
+ private Connector connector;
+
/**
* Object on External Resource before propagation takes place.
*
* null: beforeObj was not attempted to read
- * not null, but not present: beforeObj was attempted to read, but not found
+ * not null but not present: beforeObj was attempted to read, but not found
* not null and present: beforeObj value is available
*/
private Optional<ConnectorObject> beforeObj;
+ public PropagationTaskInfo(final ExternalResource externalResource) {
+ super();
+ this.externalResource = externalResource;
+ }
+
+ public Connector getConnector() {
+ return connector;
+ }
+
+ public void setConnector(final Connector connector) {
+ this.connector = connector;
+ }
+
+ public ExternalResource getExternalResource() {
+ return externalResource;
+ }
+
+ @Override
+ public String getResource() {
+ return externalResource.getKey();
+ }
+
+ @Override
+ public void setResource(final String resource) {
+ throw new IllegalArgumentException("Cannot set ExternalResource on " + getClass().getName());
+ }
+
public Optional<ConnectorObject> getBeforeObj() {
return beforeObj;
}
@@ -42,4 +77,32 @@ public class PropagationTaskInfo extends PropagationTaskTO {
public void setBeforeObj(final Optional<ConnectorObject> beforeObj) {
this.beforeObj = beforeObj;
}
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder().
+ appendSuper(super.hashCode()).
+ append(externalResource.getKey()).
+ append(beforeObj).
+ build();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final PropagationTaskInfo other = (PropagationTaskInfo) obj;
+ return new EqualsBuilder().
+ appendSuper(super.equals(obj)).
+ append(externalResource.getKey(), other.externalResource.getKey()).
+ append(beforeObj, other.beforeObj).
+ build();
+ }
}
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfile.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfile.java
index 9aa3050..25f61b6 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfile.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfile.java
@@ -18,6 +18,7 @@
*/
package org.apache.syncope.core.provisioning.api.pushpull;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
import java.util.ArrayList;
import java.util.List;
import org.apache.syncope.common.lib.types.ConflictResolutionAction;
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/PullActions.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/PullActions.java
index ae97ff1..0070836 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/PullActions.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/PullActions.java
@@ -24,6 +24,7 @@ import org.apache.syncope.common.lib.request.AnyUR;
import org.apache.syncope.common.lib.to.LinkedAccountTO;
import org.apache.syncope.common.lib.to.RealmTO;
import org.apache.syncope.common.lib.to.EntityTO;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
import org.apache.syncope.core.persistence.api.entity.resource.Provision;
import org.identityconnectors.framework.common.objects.SyncDelta;
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/PushActions.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/PushActions.java
index 1642b0e..642d591 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/PushActions.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/PushActions.java
@@ -18,6 +18,7 @@
*/
package org.apache.syncope.core.provisioning.api.pushpull;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
import org.apache.syncope.core.persistence.api.entity.Entity;
import org.quartz.JobExecutionException;
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 c0ca069..7614291 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
@@ -18,6 +18,7 @@
*/
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;
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePushExecutor.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePushExecutor.java
index 8053a65..a9a7ce7 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePushExecutor.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePushExecutor.java
@@ -18,6 +18,7 @@
*/
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.PushTaskTO;
import org.apache.syncope.core.persistence.api.entity.Any;
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/StreamConnector.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/StreamConnector.java
new file mode 100644
index 0000000..5ff51c8
--- /dev/null
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/StreamConnector.java
@@ -0,0 +1,208 @@
+/*
+ * 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.provisioning.api.pushpull.stream;
+
+import com.fasterxml.jackson.databind.MappingIterator;
+import com.fasterxml.jackson.databind.SequenceWriter;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.core.persistence.api.entity.ConnInstance;
+import org.apache.syncope.core.provisioning.api.Connector;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.AttributeBuilder;
+import org.identityconnectors.framework.common.objects.AttributeUtil;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.identityconnectors.framework.common.objects.ObjectClassInfo;
+import org.identityconnectors.framework.common.objects.OperationOptions;
+import org.identityconnectors.framework.common.objects.SearchResult;
+import org.identityconnectors.framework.common.objects.SyncResultsHandler;
+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.springframework.util.CollectionUtils;
+
+public class StreamConnector implements Connector {
+
+ private final String keyColumn;
+
+ private final String arrayElementsSeparator;
+
+ private final MappingIterator<Map<String, String>> reader;
+
+ private final SequenceWriter writer;
+
+ public StreamConnector(
+ final String keyColumn,
+ final String arrayElementsSeparator,
+ final MappingIterator<Map<String, String>> reader,
+ final SequenceWriter writer) {
+
+ this.keyColumn = keyColumn;
+ this.arrayElementsSeparator = arrayElementsSeparator;
+ this.reader = reader;
+ this.writer = writer;
+ }
+
+ @Override
+ public Uid authenticate(final String username, final String password, final OperationOptions options) {
+ return null;
+ }
+
+ @Override
+ public ConnInstance getConnInstance() {
+ return null;
+ }
+
+ @Override
+ public Uid create(
+ final ObjectClass objectClass,
+ final Set<Attribute> attrs,
+ final OperationOptions options,
+ final AtomicReference<Boolean> propagationAttempted) {
+
+ if (writer != null) {
+ Map<String, String> row = new HashMap<>();
+ attrs.stream().filter(attr -> !AttributeUtil.isSpecial(attr)).forEach(attr -> {
+ if (CollectionUtils.isEmpty(attr.getValue()) || attr.getValue().get(0) == null) {
+ row.put(attr.getName(), null);
+ } else if (attr.getValue().size() == 1) {
+ row.put(attr.getName(), attr.getValue().get(0).toString());
+ } else if (arrayElementsSeparator == null) {
+ row.put(attr.getName(), attr.getValue().toString());
+ } else {
+ row.put(
+ attr.getName(),
+ attr.getValue().stream().map(Object::toString).
+ collect(Collectors.joining(arrayElementsSeparator)));
+ }
+ });
+ try {
+ writer.write(row);
+ } catch (IOException e) {
+ throw new IllegalStateException("Could not object " + row, e);
+ }
+ propagationAttempted.set(Boolean.TRUE);
+ }
+ return null;
+ }
+
+ @Override
+ public Uid update(
+ final ObjectClass objectClass,
+ final Uid uid,
+ final Set<Attribute> attrs,
+ final OperationOptions options,
+ final AtomicReference<Boolean> propagationAttempted) {
+
+ return null;
+ }
+
+ @Override
+ public void delete(
+ final ObjectClass objectClass,
+ final Uid uid,
+ final OperationOptions options,
+ final AtomicReference<Boolean> propagationAttempted) {
+
+ // nothing to do
+ }
+
+ @Override
+ public void sync(
+ final ObjectClass objectClass,
+ final SyncToken token,
+ final SyncResultsHandler handler,
+ final OperationOptions options) {
+
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public SyncToken getLatestSyncToken(final ObjectClass objectClass) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public ConnectorObject getObject(
+ final ObjectClass objectClass,
+ final Attribute connObjectKey,
+ final boolean ignoreCaseMatch,
+ final OperationOptions options) {
+
+ return null;
+ }
+
+ @Override
+ public SearchResult search(
+ final ObjectClass objectClass,
+ final Filter filter,
+ final SearchResultsHandler handler,
+ final OperationOptions options) {
+
+ SearchResult result = new SearchResult();
+
+ if (reader != null) {
+ while (reader.hasNext()) {
+ Map<String, String> row = reader.next();
+
+ ConnectorObjectBuilder builder = new ConnectorObjectBuilder();
+ builder.setObjectClass(objectClass);
+ builder.setUid(row.get(keyColumn));
+ builder.setName(row.get(keyColumn));
+
+ row.forEach((key, value) -> builder.addAttribute(arrayElementsSeparator == null
+ ? AttributeBuilder.build(key, value)
+ : AttributeBuilder.build(key,
+ (Object[]) StringUtils.splitByWholeSeparator(value, arrayElementsSeparator))));
+
+ handler.handle(builder.build());
+ }
+ }
+
+ return result;
+ }
+
+ @Override
+ public Set<ObjectClassInfo> getObjectClassInfo() {
+ return Set.of();
+ }
+
+ @Override
+ public void validate() {
+ // nothing to do
+ }
+
+ @Override
+ public void test() {
+ // nothing to do
+ }
+
+ @Override
+ public void dispose() {
+ // nothing to do
+ }
+}
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/stream/SyncopeStreamPullExecutor.java
similarity index 61%
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/stream/SyncopeStreamPullExecutor.java
index c0ca069..8d5c241 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/stream/SyncopeStreamPullExecutor.java
@@ -16,21 +16,24 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.provisioning.api.pushpull;
+package org.apache.syncope.core.provisioning.api.pushpull.stream;
+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.apache.syncope.common.lib.types.ConflictResolutionAction;
+import org.apache.syncope.core.persistence.api.entity.AnyType;
import org.quartz.JobExecutionException;
-@FunctionalInterface
-public interface SyncopeSinglePullExecutor {
+public interface SyncopeStreamPullExecutor {
List<ProvisioningReport> pull(
- Provision provision,
- Connector connector,
- String connObjectKey,
- String connObjectValue,
- PullTaskTO pullTaskTO) throws JobExecutionException;
+ AnyType anyType,
+ String keyColumn,
+ List<String> columns,
+ ConflictResolutionAction conflictResolutionAction,
+ String pullCorrelationRule,
+ StreamConnector connector,
+ PullTaskTO pullTaskTO)
+ throws JobExecutionException;
}
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePushExecutor.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/SyncopeStreamPushExecutor.java
similarity index 60%
copy from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePushExecutor.java
copy to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/SyncopeStreamPushExecutor.java
index 8053a65..b70a2c4 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeSinglePushExecutor.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/SyncopeStreamPushExecutor.java
@@ -16,27 +16,23 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.provisioning.api.pushpull;
+package org.apache.syncope.core.provisioning.api.pushpull.stream;
import java.util.List;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
import org.apache.syncope.common.lib.to.PushTaskTO;
import org.apache.syncope.core.persistence.api.entity.Any;
-import org.apache.syncope.core.persistence.api.entity.resource.Provision;
-import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount;
-import org.apache.syncope.core.provisioning.api.Connector;
+import org.apache.syncope.core.persistence.api.entity.AnyType;
import org.quartz.JobExecutionException;
-public interface SyncopeSinglePushExecutor {
+public interface SyncopeStreamPushExecutor {
List<ProvisioningReport> push(
- Provision provision,
- Connector connector,
- Any<?> any,
- PushTaskTO pushTaskTO) throws JobExecutionException;
-
- ProvisioningReport push(
- Provision provision,
- Connector connector,
- LinkedAccount account,
- PushTaskTO pushTaskTO) throws JobExecutionException;
+ AnyType anyType,
+ List<? extends Any<?>> anys,
+ List<String> columns,
+ StreamConnector connector,
+ PushTaskTO pushTaskTO,
+ String executor)
+ throws JobExecutionException;
}
diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/IntAttrNameParserTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/IntAttrNameParserTest.java
similarity index 75%
rename from core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/IntAttrNameParserTest.java
rename to core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/IntAttrNameParserTest.java
index b10a4b9..2a2b178 100644
--- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/IntAttrNameParserTest.java
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/IntAttrNameParserTest.java
@@ -16,31 +16,130 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.provisioning.java;
+package org.apache.syncope.core.provisioning.api;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import java.text.ParseException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
import org.apache.syncope.common.lib.types.SchemaType;
+import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
import org.apache.syncope.core.persistence.api.entity.DerSchema;
import org.apache.syncope.core.persistence.api.entity.PlainSchema;
import org.apache.syncope.core.persistence.api.entity.VirSchema;
-import org.apache.syncope.core.provisioning.api.IntAttrName;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.transaction.annotation.Transactional;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.junit.jupiter.MockitoSettings;
+import org.mockito.quality.Strictness;
+import org.springframework.util.ReflectionUtils;
-@Transactional("Master")
-public class IntAttrNameParserTest extends AbstractTest {
+@ExtendWith(MockitoExtension.class)
+@MockitoSettings(strictness = Strictness.WARN)
+public class IntAttrNameParserTest {
- @Autowired
+ private static final Map<AnyTypeKind, List<String>> FIELDS = new HashMap<>();
+
+ static {
+ FIELDS.put(AnyTypeKind.USER, Arrays.asList("key", "username"));
+ FIELDS.put(AnyTypeKind.GROUP, Arrays.asList("key", "name", "userOwner"));
+ FIELDS.put(AnyTypeKind.ANY_OBJECT, Arrays.asList("key", "name"));
+ }
+
+ @Mock
+ private PlainSchemaDAO plainSchemaDAO;
+
+ @Mock
+ private DerSchemaDAO derSchemaDAO;
+
+ @Mock
+ private VirSchemaDAO virSchemaDAO;
+
+ @Mock
+ private AnyUtilsFactory anyUtilsFactory;
+
+ @Mock
+ private AnyUtils anyUtils;
+
+ @InjectMocks
private IntAttrNameParser intAttrNameParser;
+ @BeforeEach
+ public void initMocks() throws NoSuchFieldException {
+ MockitoAnnotations.initMocks(this);
+
+ when(anyUtilsFactory.getInstance(any(AnyTypeKind.class))).thenAnswer(ic -> {
+ when(anyUtils.anyTypeKind()).thenReturn(ic.getArgument(0));
+ return anyUtils;
+ });
+ when(anyUtils.getField(anyString())).thenAnswer(ic -> {
+ String field = ic.getArgument(0);
+ return FIELDS.get(anyUtils.anyTypeKind()).contains(field)
+ ? ReflectionUtils.findField(getClass(), "anyUtils")
+ : null;
+ });
+ when(plainSchemaDAO.find(anyString())).thenAnswer(ic -> {
+ String schemaName = ic.getArgument(0);
+ switch (schemaName) {
+ case "email":
+ case "firstname":
+ case "location":
+ PlainSchema schema = mock(PlainSchema.class);
+ when(schema.getKey()).thenReturn(schemaName);
+ when(schema.getType()).thenReturn(AttrSchemaType.String);
+ return schema;
+
+ default:
+ return null;
+ }
+ });
+ when(derSchemaDAO.find(anyString())).thenAnswer(ic -> {
+ String schemaName = ic.getArgument(0);
+ switch (schemaName) {
+ case "cn":
+ DerSchema schema = mock(DerSchema.class);
+ when(schema.getKey()).thenReturn(ic.getArgument(0));
+ return schema;
+
+ default:
+ return null;
+ }
+ });
+ when(virSchemaDAO.find(anyString())).thenAnswer(ic -> {
+ String schemaName = ic.getArgument(0);
+ switch (schemaName) {
+ case "rvirtualdata":
+ VirSchema schema = mock(VirSchema.class);
+ when(schema.getKey()).thenReturn(ic.getArgument(0));
+ return schema;
+
+ default:
+ return null;
+ }
+ });
+ }
+
@Test
public void ownFields() throws ParseException {
IntAttrName intAttrName = intAttrNameParser.parse("key", AnyTypeKind.USER);
@@ -231,8 +330,8 @@ public class IntAttrNameParserTest extends AbstractTest {
@Test
public void relationship() throws ParseException {
- IntAttrName intAttrName = intAttrNameParser.parse("relationships[inclusion][PRINTER].location",
- AnyTypeKind.USER);
+ IntAttrName intAttrName = intAttrNameParser.parse(
+ "relationships[inclusion][PRINTER].location", AnyTypeKind.USER);
assertNotNull(intAttrName);
assertEquals(AnyTypeKind.ANY_OBJECT, intAttrName.getAnyTypeKind());
assertNull(intAttrName.getField());
diff --git a/core/provisioning-java/pom.xml b/core/provisioning-java/pom.xml
index a9610df..146e9d8 100644
--- a/core/provisioning-java/pom.xml
+++ b/core/provisioning-java/pom.xml
@@ -45,11 +45,6 @@ under the License.
</dependency>
<dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-jexl3</artifactId>
- </dependency>
-
- <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
@@ -142,6 +137,11 @@ under the License.
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java
index 16faf61..ad89d8f 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnectorFacadeProxy.java
@@ -24,7 +24,6 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.syncope.common.lib.types.ConnectorCapability;
import org.apache.syncope.core.persistence.api.entity.ConnInstance;
@@ -33,7 +32,6 @@ import org.apache.syncope.core.provisioning.api.utils.ConnPoolConfUtils;
import org.apache.syncope.core.provisioning.api.Connector;
import org.apache.syncope.core.provisioning.api.TimeoutException;
import org.apache.syncope.core.provisioning.api.pushpull.ReconFilterBuilder;
-import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
import org.apache.syncope.core.spring.ApplicationContextProvider;
import org.identityconnectors.common.security.GuardedByteArray;
import org.identityconnectors.common.security.GuardedString;
@@ -49,9 +47,6 @@ import org.identityconnectors.framework.common.objects.ObjectClassInfo;
import org.identityconnectors.framework.common.objects.OperationOptions;
import org.identityconnectors.framework.common.objects.OperationOptionsBuilder;
import org.identityconnectors.framework.common.objects.SearchResult;
-import org.identityconnectors.framework.common.objects.SortKey;
-import org.identityconnectors.framework.common.objects.SyncDeltaBuilder;
-import org.identityconnectors.framework.common.objects.SyncDeltaType;
import org.identityconnectors.framework.common.objects.SyncResultsHandler;
import org.identityconnectors.framework.common.objects.SyncToken;
import org.identityconnectors.framework.common.objects.Uid;
@@ -310,7 +305,7 @@ public class ConnectorFacadeProxy implements Connector {
final SyncResultsHandler handler,
final OperationOptions options) {
- filteredReconciliation(objectClass, null, handler, options);
+ Connector.super.fullReconciliation(objectClass, handler, options);
}
@Transactional
@@ -321,29 +316,7 @@ public class ConnectorFacadeProxy implements Connector {
final SyncResultsHandler handler,
final OperationOptions options) {
- Filter filter = null;
- OperationOptions actualOptions = options;
- if (filterBuilder != null) {
- filter = filterBuilder.build();
- actualOptions = filterBuilder.build(actualOptions);
- }
-
- search(objectClass, filter, new SearchResultsHandler() {
-
- @Override
- public void handleResult(final SearchResult result) {
- // nothing to do
- }
-
- @Override
- public boolean handle(final ConnectorObject object) {
- return handler.handle(new SyncDeltaBuilder().
- setObject(object).
- setDeltaType(SyncDeltaType.CREATE_OR_UPDATE).
- setToken(new SyncToken("")).
- build());
- }
- }, actualOptions);
+ Connector.super.filteredReconciliation(objectClass, filterBuilder, handler, options);
}
@Override
@@ -477,29 +450,6 @@ public class ConnectorFacadeProxy implements Connector {
}
@Override
- public SearchResult search(
- final ObjectClass objectClass,
- final Filter filter,
- final SearchResultsHandler handler,
- final int pageSize,
- final String pagedResultsCookie,
- final List<OrderByClause> orderBy,
- final OperationOptions options) {
-
- OperationOptionsBuilder builder = new OperationOptionsBuilder().setPageSize(pageSize).setPagedResultsOffset(-1);
- if (pagedResultsCookie != null) {
- builder.setPagedResultsCookie(pagedResultsCookie);
- }
- builder.setSortKeys(orderBy.stream().map(clause
- -> new SortKey(clause.getField(), clause.getDirection() == OrderByClause.Direction.ASC)).
- collect(Collectors.toList()));
-
- builder.setAttributesToGet(options.getAttributesToGet());
-
- return search(objectClass, filter, handler, builder.build());
- }
-
- @Override
public void dispose() {
connector.dispose();
}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
index bb86889..49da4df 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
@@ -45,7 +45,7 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
import org.apache.syncope.core.provisioning.api.VirAttrHandler;
import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
-import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DerAttrHandlerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DerAttrHandlerImpl.java
index 0f4757f..27dbef4 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DerAttrHandlerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DerAttrHandlerImpl.java
@@ -23,13 +23,13 @@ import java.util.Map;
import java.util.Set;
import org.apache.commons.jexl3.JexlContext;
import org.apache.commons.jexl3.MapContext;
-import org.apache.syncope.core.provisioning.java.jexl.JexlUtils;
import org.apache.syncope.core.persistence.api.entity.Any;
import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
import org.apache.syncope.core.persistence.api.entity.DerSchema;
import org.apache.syncope.core.persistence.api.entity.GroupableRelatable;
import org.apache.syncope.core.persistence.api.entity.Membership;
import org.apache.syncope.core.provisioning.api.DerAttrHandler;
+import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
index 63fd67d..e84d043 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
@@ -18,6 +18,7 @@
*/
package org.apache.syncope.core.provisioning.java;
+import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Base64;
@@ -28,6 +29,8 @@ import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
+import org.apache.commons.jexl3.JexlContext;
+import org.apache.commons.jexl3.MapContext;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
@@ -101,7 +104,9 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.apache.syncope.core.provisioning.api.data.ItemTransformer;
+import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
import org.identityconnectors.framework.common.objects.Name;
+import org.identityconnectors.framework.common.objects.Uid;
@Component
public class MappingManagerImpl implements MappingManager {
@@ -182,6 +187,87 @@ public class MappingManagerImpl implements MappingManager {
return connObjectKey;
}
+ private static Name getName(final String evalConnObjectLink, final String connObjectKey) {
+ // If connObjectLink evaluates to an empty string, just use the provided connObjectKey as Name(),
+ // otherwise evaluated connObjectLink expression is taken as Name().
+ Name name;
+ if (StringUtils.isBlank(evalConnObjectLink)) {
+ // add connObjectKey as __NAME__ attribute ...
+ LOG.debug("Add connObjectKey [{}] as {}", connObjectKey, Name.NAME);
+ name = new Name(connObjectKey);
+ } else {
+ LOG.debug("Add connObjectLink [{}] as {}", evalConnObjectLink, Name.NAME);
+ name = new Name(evalConnObjectLink);
+
+ // connObjectKey not propagated: it will be used to set the value for __UID__ attribute
+ LOG.debug("connObjectKey will be used just as {} attribute", Uid.NAME);
+ }
+
+ return name;
+ }
+
+ /**
+ * Build __NAME__ for propagation.
+ * First look if there is a defined connObjectLink for the given resource (and in
+ * this case evaluate as JEXL); otherwise, take given connObjectKey.
+ *
+ * @param any given any object
+ * @param provision external resource
+ * @param connObjectKey connector object key
+ * @return the value to be propagated as __NAME__
+ */
+ private Name evaluateNAME(final Any<?> any, final Provision provision, final String connObjectKey) {
+ if (StringUtils.isBlank(connObjectKey)) {
+ // LOG error but avoid to throw exception: leave it to the external resource
+ LOG.warn("Missing ConnObjectKey value for {}: ", provision.getResource());
+ }
+
+ // Evaluate connObjectKey expression
+ String connObjectLink = provision == null || provision.getMapping() == null
+ ? null
+ : provision.getMapping().getConnObjectLink();
+ String evalConnObjectLink = null;
+ if (StringUtils.isNotBlank(connObjectLink)) {
+ JexlContext jexlContext = new MapContext();
+ JexlUtils.addFieldsToContext(any, jexlContext);
+ JexlUtils.addPlainAttrsToContext(any.getPlainAttrs(), jexlContext);
+ JexlUtils.addDerAttrsToContext(any, derAttrHandler, jexlContext);
+ evalConnObjectLink = JexlUtils.evaluate(connObjectLink, jexlContext);
+ }
+
+ return getName(evalConnObjectLink, connObjectKey);
+ }
+
+ /**
+ * Build __NAME__ for propagation.
+ * First look if there is a defined connObjectLink for the given resource (and in
+ * this case evaluate as JEXL); otherwise, take given connObjectKey.
+ *
+ * @param realm given any object
+ * @param orgUnit external resource
+ * @param connObjectKey connector object key
+ * @return the value to be propagated as __NAME__
+ */
+ private Name evaluateNAME(final Realm realm, final OrgUnit orgUnit, final String connObjectKey) {
+ if (StringUtils.isBlank(connObjectKey)) {
+ // LOG error but avoid to throw exception: leave it to the external resource
+ LOG.warn("Missing ConnObjectKey value for {}: ", orgUnit.getResource());
+ }
+
+ // Evaluate connObjectKey expression
+ String connObjectLink = orgUnit == null
+ ? null
+ : orgUnit.getConnObjectLink();
+ String evalConnObjectLink = null;
+ if (StringUtils.isNotBlank(connObjectLink)) {
+ JexlContext jexlContext = new MapContext();
+ JexlUtils.addFieldsToContext(realm, jexlContext);
+ evalConnObjectLink = JexlUtils.evaluate(connObjectLink, jexlContext);
+ }
+
+ return getName(evalConnObjectLink, connObjectKey);
+ }
+
@Transactional(readOnly = true)
@Override
public Pair<String, Set<Attribute>> prepareAttrs(
@@ -225,7 +311,7 @@ public class MappingManagerImpl implements MappingManager {
attributes.remove(connObjectKeyAttr);
attributes.add(AttributeBuilder.build(connObjectKeyItem.getExtAttrName(), connObjectKeyValue[0]));
}
- Name name = MappingUtils.evaluateNAME(any, provision, connObjectKeyValue[0]);
+ Name name = evaluateNAME(any, provision, connObjectKeyValue[0]);
attributes.add(name);
if (connObjectKeyAttr == null
&& connObjectKeyValue[0] != null && !connObjectKeyValue[0].equals(name.getNameValue())) {
@@ -300,7 +386,7 @@ public class MappingManagerImpl implements MappingManager {
attributes.remove(connObjectKeyExtAttr);
attributes.add(AttributeBuilder.build(connObjectKeyItem.getExtAttrName(), connObjectKey));
}
- Name name = MappingUtils.evaluateNAME(user, provision, connObjectKey);
+ Name name = evaluateNAME(user, provision, connObjectKey);
attributes.add(name);
if (!connObjectKey.equals(name.getNameValue()) && connObjectKeyExtAttr == null) {
attributes.add(AttributeBuilder.build(connObjectKeyItem.getExtAttrName(), connObjectKey));
@@ -378,7 +464,7 @@ public class MappingManagerImpl implements MappingManager {
attributes.remove(connObjectKeyAttr);
attributes.add(AttributeBuilder.build(connObjectKeyItem.get().getExtAttrName(), connObjectKeyValue[0]));
}
- attributes.add(MappingUtils.evaluateNAME(realm, orgUnit, connObjectKeyValue[0]));
+ attributes.add(evaluateNAME(realm, orgUnit, connObjectKeyValue[0]));
}
return Pair.of(connObjectKeyValue[0], attributes);
@@ -634,24 +720,24 @@ public class MappingManagerImpl implements MappingManager {
default:
try {
- Object fieldValue = FieldUtils.readField(ref, intAttrName.getField(), true);
- if (fieldValue instanceof Date) {
- // needed because ConnId does not natively supports the Date type
- attrValue.setStringValue(DateFormatUtils.ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT.
- format((Date) fieldValue));
- } else if (Boolean.TYPE.isInstance(fieldValue)) {
- attrValue.setBooleanValue((Boolean) fieldValue);
- } else if (Double.TYPE.isInstance(fieldValue) || Float.TYPE.isInstance(fieldValue)) {
- attrValue.setDoubleValue((Double) fieldValue);
- } else if (Long.TYPE.isInstance(fieldValue) || Integer.TYPE.isInstance(fieldValue)) {
- attrValue.setLongValue((Long) fieldValue);
- } else {
- attrValue.setStringValue(fieldValue.toString());
- }
- values.add(attrValue);
- } catch (Exception e) {
- LOG.error("Could not read value of '{}' from {}", intAttrName.getField(), ref, e);
+ Object fieldValue = FieldUtils.readField(ref, intAttrName.getField(), true);
+ if (fieldValue instanceof Date) {
+ // needed because ConnId does not natively supports the Date type
+ attrValue.setStringValue(DateFormatUtils.ISO_8601_EXTENDED_DATETIME_TIME_ZONE_FORMAT.
+ format((Date) fieldValue));
+ } else if (Boolean.TYPE.isInstance(fieldValue)) {
+ attrValue.setBooleanValue((Boolean) fieldValue);
+ } else if (Double.TYPE.isInstance(fieldValue) || Float.TYPE.isInstance(fieldValue)) {
+ attrValue.setDoubleValue((Double) fieldValue);
+ } else if (Long.TYPE.isInstance(fieldValue) || Integer.TYPE.isInstance(fieldValue)) {
+ attrValue.setLongValue((Long) fieldValue);
+ } else {
+ attrValue.setStringValue(fieldValue.toString());
}
+ values.add(attrValue);
+ } catch (Exception e) {
+ LOG.error("Could not read value of '{}' from {}", intAttrName.getField(), ref, e);
+ }
}
} else if (intAttrName.getSchemaType() != null) {
switch (intAttrName.getSchemaType()) {
@@ -758,8 +844,8 @@ public class MappingManagerImpl implements MappingManager {
PlainAttrGetter.DEFAULT);
}
- return Optional.ofNullable(preparedAttr)
- .map(attr -> MappingUtils.evaluateNAME(any, provision, attr.getKey()).getNameValue()).orElse(null);
+ return Optional.ofNullable(preparedAttr).
+ map(attr -> evaluateNAME(any, provision, attr.getKey()).getNameValue()).orElse(null);
}
@Transactional(readOnly = true)
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java
index 8b14cd9..92507b0 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java
@@ -29,6 +29,7 @@ import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager;
import org.apache.syncope.core.provisioning.api.AuditManager;
import org.apache.syncope.core.provisioning.api.ConnIdBundleManager;
import org.apache.syncope.core.provisioning.api.GroupProvisioningManager;
+import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
import org.apache.syncope.core.provisioning.api.cache.VirAttrCache;
import org.apache.syncope.core.provisioning.api.job.JobManager;
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 4f9649a..baa598f 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
@@ -73,8 +73,8 @@ import org.apache.syncope.core.provisioning.api.MappingManager;
import org.apache.syncope.core.provisioning.api.PlainAttrGetter;
import org.apache.syncope.core.provisioning.api.PropagationByResource;
import org.apache.syncope.core.provisioning.api.VirAttrHandler;
-import org.apache.syncope.core.provisioning.java.IntAttrNameParser;
-import org.apache.syncope.core.provisioning.java.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
+import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -226,7 +226,7 @@ abstract class AbstractAnyDataBinder {
AccountGetter.DEFAULT,
PlainAttrGetter.DEFAULT);
if (intValues.getRight().isEmpty()
- && JexlUtils.evaluateMandatoryCondition(mapItem.getMandatoryCondition(), any)) {
+ && JexlUtils.evaluateMandatoryCondition(mapItem.getMandatoryCondition(), any, derAttrHandler)) {
missingAttrNames.add(mapItem.getIntAttrName());
}
@@ -256,7 +256,7 @@ abstract class AbstractAnyDataBinder {
return reqValMissing;
}
- private static void checkMandatory(
+ private void checkMandatory(
final PlainSchema schema,
final PlainAttr<?> attr,
final Any<?> any,
@@ -264,7 +264,7 @@ abstract class AbstractAnyDataBinder {
if (attr == null
&& !schema.isReadonly()
- && JexlUtils.evaluateMandatoryCondition(schema.getMandatoryCondition(), any)) {
+ && JexlUtils.evaluateMandatoryCondition(schema.getMandatoryCondition(), any, derAttrHandler)) {
LOG.error("Mandatory schema " + schema.getKey() + " not provided with values");
@@ -272,14 +272,13 @@ abstract class AbstractAnyDataBinder {
}
}
- private static SyncopeClientException checkMandatory(final Any<?> any, final AnyUtils anyUtils) {
+ private SyncopeClientException checkMandatory(final Any<?> any, final AnyUtils anyUtils) {
SyncopeClientException reqValMissing = SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing);
// Check if there is some mandatory schema defined for which no value has been provided
AllowedSchemas<PlainSchema> allowedPlainSchemas = anyUtils.dao().findAllowedSchemas(any, PlainSchema.class);
- allowedPlainSchemas.getForSelf()
- .forEach(schema -> checkMandatory(schema, any.getPlainAttr(schema.getKey())
- .orElse(null), any, reqValMissing));
+ allowedPlainSchemas.getForSelf().forEach(schema -> checkMandatory(
+ schema, any.getPlainAttr(schema.getKey()).orElse(null), any, reqValMissing));
if (any instanceof GroupableRelatable) {
allowedPlainSchemas.getForMemberships().forEach((group, schemas) -> {
GroupableRelatable<?, ?, ?, ?, ?> groupable = GroupableRelatable.class.cast(any);
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImpl.java
index e3d69e5..997b7d6 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImpl.java
@@ -32,11 +32,16 @@ import org.apache.syncope.core.persistence.api.entity.Any;
import org.apache.syncope.core.persistence.api.entity.Entity;
import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
import org.apache.syncope.core.persistence.api.entity.resource.Item;
-import org.apache.syncope.core.provisioning.java.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.DerAttrHandler;
+import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
import org.apache.syncope.core.provisioning.api.data.JEXLItemTransformer;
+import org.springframework.beans.factory.annotation.Autowired;
public class JEXLItemTransformerImpl implements JEXLItemTransformer {
+ @Autowired
+ private DerAttrHandler derAttrHandler;
+
private String propagationJEXL;
private String pullJEXL;
@@ -67,7 +72,7 @@ public class JEXLItemTransformerImpl implements JEXLItemTransformer {
JexlUtils.addFieldsToContext(entity, jexlContext);
if (entity instanceof Any) {
JexlUtils.addPlainAttrsToContext(((Any<?>) entity).getPlainAttrs(), jexlContext);
- JexlUtils.addDerAttrsToContext(((Any<?>) entity), jexlContext);
+ JexlUtils.addDerAttrsToContext(((Any<?>) entity), derAttrHandler, jexlContext);
}
}
jexlContext.set("value", originalValue);
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderImpl.java
index c06d18d..0a5a695 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/NotificationDataBinderImpl.java
@@ -36,7 +36,7 @@ import org.apache.syncope.core.persistence.api.entity.AnyAbout;
import org.apache.syncope.core.persistence.api.entity.AnyType;
import org.apache.syncope.core.persistence.api.entity.Implementation;
import org.apache.syncope.core.persistence.api.entity.MailTemplate;
-import org.apache.syncope.core.provisioning.java.IntAttrNameParser;
+import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
index 278f31b..96dad29 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
@@ -50,7 +50,7 @@ import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
import org.apache.syncope.core.persistence.api.entity.resource.Mapping;
import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
-import org.apache.syncope.core.provisioning.java.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
@@ -67,7 +67,7 @@ import org.apache.syncope.core.persistence.api.entity.resource.Item;
import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
import org.apache.syncope.core.persistence.api.entity.resource.OrgUnitItem;
import org.apache.syncope.core.persistence.api.entity.resource.Provision;
-import org.apache.syncope.core.provisioning.java.IntAttrNameParser;
+import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
import org.apache.syncope.core.provisioning.api.IntAttrName;
import org.apache.syncope.core.provisioning.api.data.ResourceDataBinder;
import org.identityconnectors.framework.common.objects.ObjectClass;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SchemaDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SchemaDataBinderImpl.java
index 38350c4..e5d48b0 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SchemaDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SchemaDataBinderImpl.java
@@ -33,7 +33,7 @@ import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
import org.apache.syncope.core.persistence.api.entity.DerSchema;
import org.apache.syncope.core.persistence.api.entity.PlainSchema;
import org.apache.syncope.core.persistence.api.entity.VirSchema;
-import org.apache.syncope.core.provisioning.java.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO;
import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
index efc039e..1d50916 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
@@ -19,11 +19,9 @@
package org.apache.syncope.core.provisioning.java.data;
import java.util.Collection;
-import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -69,7 +67,6 @@ import org.apache.syncope.core.persistence.api.entity.Privilege;
import org.apache.syncope.core.persistence.api.entity.Realm;
import org.apache.syncope.core.persistence.api.entity.RelationshipType;
import org.apache.syncope.core.persistence.api.entity.Role;
-import org.apache.syncope.core.persistence.api.entity.VirSchema;
import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
import org.apache.syncope.core.persistence.api.entity.user.LAPlainAttr;
@@ -726,14 +723,11 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
userTO.setSecurityQuestion(user.getSecurityQuestion().getKey());
}
- Map<VirSchema, List<String>> virAttrValues = details
- ? virAttrHandler.getValues(user)
- : Collections.<VirSchema, List<String>>emptyMap();
fillTO(userTO, user.getRealm().getFullPath(),
user.getAuxClasses(),
user.getPlainAttrs(),
derAttrHandler.getValues(user),
- virAttrValues,
+ details ? virAttrHandler.getValues(user) : Map.of(),
userDAO.findAllResources(user),
details);
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java
index 37a13e7..e1f9220 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java
@@ -73,8 +73,8 @@ import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent;
import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
import org.apache.syncope.core.provisioning.api.notification.RecipientsProvider;
-import org.apache.syncope.core.provisioning.java.IntAttrNameParser;
-import org.apache.syncope.core.provisioning.java.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
+import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
import org.apache.syncope.core.spring.ImplementationManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 aa7bde0..7895244 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
@@ -338,12 +338,21 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
}
@Override
- public TaskExec execute(final PropagationTaskInfo taskInfo, final PropagationReporter reporter,
+ public TaskExec execute(
+ final PropagationTaskInfo taskInfo,
+ final PropagationReporter reporter,
final String executor) {
+
PropagationTask task;
if (taskInfo.getKey() == null) {
+ // double-checks that provided External Resource is valid, for further actions
+ ExternalResource resource = resourceDAO.find(taskInfo.getResource());
+ if (resource == null) {
+ resource = taskInfo.getExternalResource();
+ }
+
task = entityFactory.newEntity(PropagationTask.class);
- task.setResource(resourceDAO.find(taskInfo.getResource()));
+ task.setResource(resource);
task.setObjectClassName(taskInfo.getObjectClassName());
task.setAnyTypeKind(taskInfo.getAnyTypeKind());
task.setAnyType(taskInfo.getAnyType());
@@ -360,9 +369,11 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
}
task.setAttributes(attributes);
- List<PropagationActions> actions = getPropagationActions(task.getResource());
+ Connector connector = taskInfo.getConnector() == null
+ ? connFactory.getConnector(task.getResource())
+ : taskInfo.getConnector();
- String resource = task.getResource().getKey();
+ List<PropagationActions> actions = getPropagationActions(task.getResource());
Date start = new Date();
@@ -382,12 +393,10 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
Provision provision = null;
OrgUnit orgUnit = null;
Uid uid = null;
- Connector connector = null;
Result result;
try {
provision = task.getResource().getProvision(new ObjectClass(task.getObjectClassName())).orElse(null);
orgUnit = task.getResource().getOrgUnit();
- connector = connFactory.getConnector(task.getResource());
if (taskInfo.getBeforeObj() == null) {
// Try to read remote object BEFORE any actual operation
@@ -425,7 +434,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
result = Result.SUCCESS;
} catch (Exception e) {
result = Result.FAILURE;
- LOG.error("Exception during provision on resource " + resource, e);
+ LOG.error("Exception during provision on resource " + task.getResource().getKey(), e);
if (e instanceof ConnectorException && e.getCause() != null) {
taskExecutionMessage = e.getCause().getMessage();
@@ -509,12 +518,12 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
String anyTypeKind = task.getAnyTypeKind() == null ? "realm" : task.getAnyTypeKind().name().toLowerCase();
String operation = task.getOperation().name().toLowerCase();
boolean notificationsAvailable = notificationManager.notificationsAvailable(
- AuditElements.EventCategoryType.PROPAGATION, anyTypeKind, resource, operation);
+ AuditElements.EventCategoryType.PROPAGATION, anyTypeKind, task.getResource().getKey(), operation);
boolean auditRequested = auditManager.auditRequested(
AuthContextUtils.getUsername(),
AuditElements.EventCategoryType.PROPAGATION,
anyTypeKind,
- resource,
+ task.getResource().getKey(),
operation);
if (notificationsAvailable || auditRequested) {
@@ -523,7 +532,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
AuthContextUtils.getUsername(),
AuditElements.EventCategoryType.PROPAGATION,
anyTypeKind,
- resource,
+ task.getResource().getKey(),
operation,
result,
beforeObj,
@@ -534,7 +543,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
AuthContextUtils.getUsername(),
AuditElements.EventCategoryType.PROPAGATION,
anyTypeKind,
- resource,
+ task.getResource().getKey(),
operation,
result,
beforeObj,
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java
index 45c5f63..854340d 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java
@@ -32,11 +32,12 @@ import org.apache.syncope.core.persistence.api.dao.UserDAO;
import org.apache.syncope.core.persistence.api.entity.group.Group;
import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
import org.apache.syncope.core.persistence.api.entity.user.User;
-import org.apache.syncope.core.provisioning.java.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
import org.apache.syncope.core.persistence.api.dao.GroupDAO;
import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
import org.apache.syncope.core.persistence.api.entity.resource.Provision;
+import org.apache.syncope.core.provisioning.api.DerAttrHandler;
import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
import org.identityconnectors.framework.common.objects.Attribute;
import org.identityconnectors.framework.common.objects.AttributeBuilder;
@@ -58,6 +59,9 @@ public class LDAPMembershipPropagationActions implements PropagationActions {
protected static final Logger LOG = LoggerFactory.getLogger(LDAPMembershipPropagationActions.class);
@Autowired
+ protected DerAttrHandler derAttrHandler;
+
+ @Autowired
protected AnyTypeDAO anyTypeDAO;
@Autowired
@@ -119,11 +123,9 @@ public class LDAPMembershipPropagationActions implements PropagationActions {
Attribute beforeLdapGroups = beforeObj.getAttributeByName(getGroupMembershipAttrName());
LOG.debug("Memberships not managed by Syncope: {}", beforeLdapGroups);
- for (Object value : beforeLdapGroups.getValue()) {
- if (!connObjectLinks.contains(String.valueOf(value))) {
- groups.add(String.valueOf(value));
- }
- }
+ beforeLdapGroups.getValue().stream().
+ filter(value -> !connObjectLinks.contains(String.valueOf(value))).
+ forEach(value -> groups.add(String.valueOf(value)));
}
}
LOG.debug("Add ldapGroups to attributes: {}" + groups);
@@ -136,13 +138,13 @@ public class LDAPMembershipPropagationActions implements PropagationActions {
}
}
- private static String evaluateGroupConnObjectLink(final String connObjectLinkTemplate, final Group group) {
+ private String evaluateGroupConnObjectLink(final String connObjectLinkTemplate, final Group group) {
LOG.debug("Evaluating connObjectLink for {}", group);
JexlContext jexlContext = new MapContext();
JexlUtils.addFieldsToContext(group, jexlContext);
JexlUtils.addPlainAttrsToContext(group.getPlainAttrs(), jexlContext);
- JexlUtils.addDerAttrsToContext(group, jexlContext);
+ JexlUtils.addDerAttrsToContext(group, derAttrHandler, jexlContext);
return JexlUtils.evaluate(connObjectLinkTemplate, jexlContext);
}
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 0a60ba2..b6db281 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
@@ -18,7 +18,6 @@
*/
package org.apache.syncope.core.provisioning.java.propagation;
-import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
@@ -36,7 +35,6 @@ import java.util.stream.Collectors;
import javax.annotation.Resource;
import org.apache.syncope.common.lib.types.ExecStatus;
import org.apache.syncope.core.persistence.api.entity.Exec;
-import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
import org.apache.syncope.core.spring.ApplicationContextProvider;
import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
import org.apache.syncope.core.provisioning.api.propagation.PropagationException;
@@ -85,30 +83,25 @@ public class PriorityPropagationTaskExecutor extends AbstractPropagationTaskExec
final boolean nullPriorityAsync,
final String executorId) {
- Map<PropagationTaskInfo, ExternalResource> taskToResource = new HashMap<>(taskInfos.size());
List<PropagationTaskInfo> prioritizedTasks = new ArrayList<>();
int[] connRequestTimeout = { 60 };
- taskInfos.forEach(task -> {
- ExternalResource resource = resourceDAO.find(task.getResource());
- taskToResource.put(task, resource);
+ taskInfos.stream().filter(task -> task.getExternalResource().getPropagationPriority() != null).forEach(task -> {
+ prioritizedTasks.add(task);
- if (resource.getPropagationPriority() != null) {
- prioritizedTasks.add(task);
+ if (task.getExternalResource().getConnector().getConnRequestTimeout() != null
+ && connRequestTimeout[0] < task.getExternalResource().getConnector().getConnRequestTimeout()) {
- if (resource.getConnector().getConnRequestTimeout() != null
- && connRequestTimeout[0] < resource.getConnector().getConnRequestTimeout()) {
- connRequestTimeout[0] = resource.getConnector().getConnRequestTimeout();
- LOG.debug("Upgrade request connection timeout to {}", connRequestTimeout);
- }
+ connRequestTimeout[0] = task.getExternalResource().getConnector().getConnRequestTimeout();
+ LOG.debug("Upgrade request connection timeout to {}", connRequestTimeout);
}
});
- prioritizedTasks.sort(new PriorityComparator(taskToResource));
+ prioritizedTasks.sort(Comparator.comparing(task -> task.getExternalResource().getPropagationPriority()));
LOG.debug("Propagation tasks sorted by priority, for serial execution: {}", prioritizedTasks);
- Collection<PropagationTaskInfo> concurrentTasks = taskInfos.stream().
+ Set<PropagationTaskInfo> concurrentTasks = taskInfos.stream().
filter(task -> !prioritizedTasks.contains(task)).collect(Collectors.toSet());
LOG.debug("Propagation tasks for concurrent execution: {}", concurrentTasks);
@@ -171,30 +164,4 @@ public class PriorityPropagationTaskExecutor extends AbstractPropagationTaskExec
}
}
}
-
- /**
- * Compare propagation tasks according to related ExternalResource's priority.
- */
- protected static class PriorityComparator implements Comparator<PropagationTaskInfo>, Serializable {
-
- private static final long serialVersionUID = -1969355670784448878L;
-
- private final Map<PropagationTaskInfo, ExternalResource> taskToResource;
-
- public PriorityComparator(final Map<PropagationTaskInfo, ExternalResource> taskToResource) {
- this.taskToResource = taskToResource;
- }
-
- @Override
- public int compare(final PropagationTaskInfo task1, final PropagationTaskInfo task2) {
- int prop1 = taskToResource.get(task1).getPropagationPriority();
- int prop2 = taskToResource.get(task2).getPropagationPriority();
-
- return prop1 > prop2
- ? 1
- : prop1 == prop2
- ? 0
- : -1;
- }
- }
}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
index 1c38712..9f7b979 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
@@ -18,6 +18,15 @@
*/
package org.apache.syncope.core.provisioning.java.propagation;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
@@ -43,14 +52,15 @@ import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
import org.apache.syncope.core.persistence.api.entity.resource.Provision;
import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount;
import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.DerAttrHandler;
import org.apache.syncope.core.provisioning.api.MappingManager;
import org.apache.syncope.core.provisioning.api.PropagationByResource;
import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
-import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
import org.apache.syncope.core.provisioning.api.UserWorkflowResult;
+import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
-import org.apache.syncope.core.provisioning.java.jexl.JexlUtils;
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;
@@ -61,15 +71,6 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
/**
* Manage the data propagation to external resources.
@@ -85,30 +86,18 @@ public class PropagationManagerImpl implements PropagationManager {
@Autowired
protected AnyObjectDAO anyObjectDAO;
- /**
- * User DAO.
- */
@Autowired
protected UserDAO userDAO;
- /**
- * Group DAO.
- */
@Autowired
protected GroupDAO groupDAO;
- /**
- * Resource DAO.
- */
@Autowired
protected ExternalResourceDAO resourceDAO;
@Autowired
protected EntityFactory entityFactory;
- /**
- * ConnObjectUtils.
- */
@Autowired
protected ConnObjectUtils connObjectUtils;
@@ -116,6 +105,9 @@ public class PropagationManagerImpl implements PropagationManager {
protected MappingManager mappingManager;
@Autowired
+ protected DerAttrHandler derAttrHandler;
+
+ @Autowired
protected AnyUtilsFactory anyUtilsFactory;
protected AnyDAO<? extends Any<?>> dao(final AnyTypeKind kind) {
@@ -388,17 +380,18 @@ public class PropagationManagerImpl implements PropagationManager {
return createTasks(any, null, false, false, true, localPropByRes, propByLinkedAccount, null);
}
- protected PropagationTaskInfo newTask(
+ @Override
+ public PropagationTaskInfo newTask(
+ final DerAttrHandler derAttrHandler,
final Any<?> any,
- final String resource,
+ final ExternalResource resource,
final ResourceOperation operation,
final Provision provision,
final boolean deleteOnResource,
final Stream<? extends Item> mappingItems,
final Pair<String, Set<Attribute>> preparedAttrs) {
- PropagationTaskInfo task = new PropagationTaskInfo();
- task.setResource(resource);
+ PropagationTaskInfo task = new PropagationTaskInfo(resource);
task.setObjectClassName(provision.getObjectClass().getObjectClassValue());
task.setAnyTypeKind(any.getType().getKind());
task.setAnyType(any.getType().getKey());
@@ -413,15 +406,16 @@ public class PropagationManagerImpl implements PropagationManager {
List<String> mandatoryMissing = new ArrayList<>();
List<String> mandatoryNullOrEmpty = new ArrayList<>();
mappingItems.filter(item -> (!item.isConnObjectKey()
- && JexlUtils.evaluateMandatoryCondition(item.getMandatoryCondition(), any))).forEach(item -> {
-
- Attribute attr = AttributeUtil.find(item.getExtAttrName(), preparedAttrs.getRight());
- if (attr == null) {
- mandatoryMissing.add(item.getExtAttrName());
- } else if (CollectionUtils.isEmpty(attr.getValue())) {
- mandatoryNullOrEmpty.add(item.getExtAttrName());
- }
- });
+ && JexlUtils.evaluateMandatoryCondition(item.getMandatoryCondition(), any, derAttrHandler))).
+ forEach(item -> {
+
+ Attribute attr = AttributeUtil.find(item.getExtAttrName(), preparedAttrs.getRight());
+ if (attr == null) {
+ mandatoryMissing.add(item.getExtAttrName());
+ } else if (CollectionUtils.isEmpty(attr.getValue())) {
+ mandatoryNullOrEmpty.add(item.getExtAttrName());
+ }
+ });
if (!mandatoryMissing.isEmpty()) {
preparedAttrs.getRight().add(AttributeBuilder.build(
PropagationTaskExecutor.MANDATORY_MISSING_ATTR_NAME, mandatoryMissing));
@@ -528,8 +522,9 @@ public class PropagationManagerImpl implements PropagationManager {
}
PropagationTaskInfo task = newTask(
+ derAttrHandler,
any,
- resourceKey,
+ resource,
operation,
provision,
deleteOnResource,
@@ -571,8 +566,9 @@ public class PropagationManagerImpl implements PropagationManager {
AnyTypeKind.USER.name(), account.getResource());
} else {
PropagationTaskInfo accountTask = newTask(
+ derAttrHandler,
user,
- account.getResource().getKey(),
+ account.getResource(),
operation,
provision,
deleteOnResource,
@@ -620,8 +616,7 @@ public class PropagationManagerImpl implements PropagationManager {
LOG.warn("Requesting propagation for {} but no ConnObjectLink provided for {}",
realm.getFullPath(), resource);
} else {
- PropagationTaskInfo task = new PropagationTaskInfo();
- task.setResource(resource.getKey());
+ PropagationTaskInfo task = new PropagationTaskInfo(resource);
task.setObjectClassName(orgUnit.getObjectClass().getObjectClassValue());
task.setEntityKey(realm.getKey());
task.setOperation(operation);
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractProvisioningJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractProvisioningJobDelegate.java
index 5f06bfd..25a85c4 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractProvisioningJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractProvisioningJobDelegate.java
@@ -22,6 +22,7 @@ import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Objects;
import javax.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.types.AnyTypeKind;
@@ -36,7 +37,7 @@ import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask;
import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
import org.apache.syncope.core.provisioning.api.Connector;
import org.apache.syncope.core.provisioning.api.ConnectorFactory;
-import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
import org.apache.syncope.core.provisioning.java.job.AbstractSchedTaskJobDelegate;
import org.apache.syncope.core.provisioning.java.job.TaskJob;
import org.quartz.JobExecutionException;
@@ -50,6 +51,37 @@ public abstract class AbstractProvisioningJobDelegate<T extends ProvisioningTask
private static final String LINKED_ACCOUNT = "LINKED_ACCOUNT";
+ /**
+ * Helper method to invoke logging per provisioning result, for the given trace level.
+ *
+ * @param results provisioning results
+ * @param level trace level
+ * @return report as string
+ */
+ public static String generate(final Collection<ProvisioningReport> results, final TraceLevel level) {
+ StringBuilder sb = new StringBuilder();
+
+ results.stream().map(result -> {
+ if (level == TraceLevel.SUMMARY) {
+ // No per entry log in this case.
+ return null;
+ } else if (level == TraceLevel.FAILURES && result.getStatus() == ProvisioningReport.Status.FAILURE) {
+ // only report failures
+ return String.format("Failed %s (key/name): %s/%s with message: %s",
+ result.getOperation(), result.getKey(), result.getName(), result.getMessage());
+ } else {
+ // All
+ return String.format("%s %s (key/name): %s/%s %s",
+ result.getOperation(), result.getStatus(), result.getKey(), result.getName(),
+ StringUtils.isBlank(result.getMessage())
+ ? StringUtils.EMPTY
+ : "with message: " + result.getMessage());
+ }
+ }).filter(Objects::nonNull).forEach(report -> sb.append(report).append('\n'));
+
+ return sb.toString();
+ }
+
@Resource(name = "adminUser")
protected String adminUser;
@@ -412,71 +444,71 @@ public abstract class AbstractProvisioningJobDelegate<T extends ProvisioningTask
if (includeUser) {
if (!uFailCreate.isEmpty()) {
report.append("\n\nUsers failed to create: ");
- report.append(ProvisioningReport.generate(uFailCreate, traceLevel));
+ report.append(generate(uFailCreate, traceLevel));
}
if (!uFailUpdate.isEmpty()) {
report.append("\nUsers failed to update: ");
- report.append(ProvisioningReport.generate(uFailUpdate, traceLevel));
+ report.append(generate(uFailUpdate, traceLevel));
}
if (!uFailDelete.isEmpty()) {
report.append("\nUsers failed to delete: ");
- report.append(ProvisioningReport.generate(uFailDelete, traceLevel));
+ report.append(generate(uFailDelete, traceLevel));
}
if (!laFailCreate.isEmpty()) {
report.append("\n\nAccounts failed to create: ");
- report.append(ProvisioningReport.generate(laFailCreate, traceLevel));
+ report.append(generate(laFailCreate, traceLevel));
}
if (!laFailUpdate.isEmpty()) {
report.append("\nAccounts failed to update: ");
- report.append(ProvisioningReport.generate(laFailUpdate, traceLevel));
+ report.append(generate(laFailUpdate, traceLevel));
}
if (!laFailDelete.isEmpty()) {
report.append("\nAccounts failed to delete: ");
- report.append(ProvisioningReport.generate(laFailDelete, traceLevel));
+ report.append(generate(laFailDelete, traceLevel));
}
}
if (includeGroup) {
if (!gFailCreate.isEmpty()) {
report.append("\n\nGroups failed to create: ");
- report.append(ProvisioningReport.generate(gFailCreate, traceLevel));
+ report.append(generate(gFailCreate, traceLevel));
}
if (!gFailUpdate.isEmpty()) {
report.append("\nGroups failed to update: ");
- report.append(ProvisioningReport.generate(gFailUpdate, traceLevel));
+ report.append(generate(gFailUpdate, traceLevel));
}
if (!gFailDelete.isEmpty()) {
report.append("\nGroups failed to delete: ");
- report.append(ProvisioningReport.generate(gFailDelete, traceLevel));
+ report.append(generate(gFailDelete, traceLevel));
}
}
if (includeAnyObject && !aFailCreate.isEmpty()) {
report.append("\nAny objects failed to create: ");
- report.append(ProvisioningReport.generate(aFailCreate, traceLevel));
+ report.append(generate(aFailCreate, traceLevel));
}
if (includeAnyObject && !aFailUpdate.isEmpty()) {
report.append("\nAny objects failed to update: ");
- report.append(ProvisioningReport.generate(aFailUpdate, traceLevel));
+ report.append(generate(aFailUpdate, traceLevel));
}
if (includeAnyObject && !aFailDelete.isEmpty()) {
report.append("\nAny objects failed to delete: ");
- report.append(ProvisioningReport.generate(aFailDelete, traceLevel));
+ report.append(generate(aFailDelete, traceLevel));
}
if (includeRealm) {
if (!rFailCreate.isEmpty()) {
report.append("\nRealms failed to create: ");
- report.append(ProvisioningReport.generate(rFailCreate, traceLevel));
+ report.append(generate(rFailCreate, traceLevel));
}
if (!rFailUpdate.isEmpty()) {
report.append("\nRealms failed to update: ");
- report.append(ProvisioningReport.generate(rFailUpdate, traceLevel));
+ report.append(generate(rFailUpdate, traceLevel));
}
if (!rFailDelete.isEmpty()) {
report.append("\nRealms failed to delete: ");
- report.append(ProvisioningReport.generate(rFailDelete, traceLevel));
+ report.append(generate(rFailDelete, traceLevel));
}
}
}
@@ -486,110 +518,110 @@ public abstract class AbstractProvisioningJobDelegate<T extends ProvisioningTask
if (includeUser) {
if (!uSuccCreate.isEmpty()) {
report.append("\n\nUsers created:\n").
- append(ProvisioningReport.generate(uSuccCreate, traceLevel));
+ append(generate(uSuccCreate, traceLevel));
}
if (!uSuccUpdate.isEmpty()) {
report.append("\nUsers updated:\n").
- append(ProvisioningReport.generate(uSuccUpdate, traceLevel));
+ append(generate(uSuccUpdate, traceLevel));
}
if (!uSuccDelete.isEmpty()) {
report.append("\nUsers deleted:\n").
- append(ProvisioningReport.generate(uSuccDelete, traceLevel));
+ append(generate(uSuccDelete, traceLevel));
}
if (!uSuccNone.isEmpty()) {
report.append("\nUsers no operation:\n").
- append(ProvisioningReport.generate(uSuccNone, traceLevel));
+ append(generate(uSuccNone, traceLevel));
}
if (!uIgnore.isEmpty()) {
report.append("\nUsers ignored:\n").
- append(ProvisioningReport.generate(uIgnore, traceLevel));
+ append(generate(uIgnore, traceLevel));
}
if (!laSuccCreate.isEmpty()) {
report.append("\n\nAccounts created:\n").
- append(ProvisioningReport.generate(laSuccCreate, traceLevel));
+ append(generate(laSuccCreate, traceLevel));
}
if (!laSuccUpdate.isEmpty()) {
report.append("\nAccounts updated:\n").
- append(ProvisioningReport.generate(laSuccUpdate, traceLevel));
+ append(generate(laSuccUpdate, traceLevel));
}
if (!laSuccDelete.isEmpty()) {
report.append("\nAccounts deleted:\n").
- append(ProvisioningReport.generate(laSuccDelete, traceLevel));
+ append(generate(laSuccDelete, traceLevel));
}
if (!laSuccNone.isEmpty()) {
report.append("\nAccounts no operation:\n").
- append(ProvisioningReport.generate(laSuccNone, traceLevel));
+ append(generate(laSuccNone, traceLevel));
}
if (!laIgnore.isEmpty()) {
report.append("\nAccounts ignored:\n").
- append(ProvisioningReport.generate(laIgnore, traceLevel));
+ append(generate(laIgnore, traceLevel));
}
}
if (includeGroup) {
if (!gSuccCreate.isEmpty()) {
report.append("\n\nGroups created:\n").
- append(ProvisioningReport.generate(gSuccCreate, traceLevel));
+ append(generate(gSuccCreate, traceLevel));
}
if (!gSuccUpdate.isEmpty()) {
report.append("\nGroups updated:\n").
- append(ProvisioningReport.generate(gSuccUpdate, traceLevel));
+ append(generate(gSuccUpdate, traceLevel));
}
if (!gSuccDelete.isEmpty()) {
report.append("\nGroups deleted:\n").
- append(ProvisioningReport.generate(gSuccDelete, traceLevel));
+ append(generate(gSuccDelete, traceLevel));
}
if (!gSuccNone.isEmpty()) {
report.append("\nGroups no operation:\n").
- append(ProvisioningReport.generate(gSuccNone, traceLevel));
+ append(generate(gSuccNone, traceLevel));
}
if (!gIgnore.isEmpty()) {
report.append("\nGroups ignored:\n").
- append(ProvisioningReport.generate(gIgnore, traceLevel));
+ append(generate(gIgnore, traceLevel));
}
}
if (includeAnyObject) {
if (!aSuccCreate.isEmpty()) {
report.append("\n\nAny objects created:\n").
- append(ProvisioningReport.generate(aSuccCreate, traceLevel));
+ append(generate(aSuccCreate, traceLevel));
}
if (!aSuccUpdate.isEmpty()) {
report.append("\nAny objects updated:\n").
- append(ProvisioningReport.generate(aSuccUpdate, traceLevel));
+ append(generate(aSuccUpdate, traceLevel));
}
if (!aSuccDelete.isEmpty()) {
report.append("\nAny objects deleted:\n").
- append(ProvisioningReport.generate(aSuccDelete, traceLevel));
+ append(generate(aSuccDelete, traceLevel));
}
if (!aSuccNone.isEmpty()) {
report.append("\nAny objects no operation:\n").
- append(ProvisioningReport.generate(aSuccNone, traceLevel));
+ append(generate(aSuccNone, traceLevel));
}
if (!aIgnore.isEmpty()) {
report.append("\nAny objects ignored:\n").
- append(ProvisioningReport.generate(aIgnore, traceLevel));
+ append(generate(aIgnore, traceLevel));
}
}
if (includeRealm) {
if (!rSuccCreate.isEmpty()) {
report.append("\n\nRealms created:\n").
- append(ProvisioningReport.generate(rSuccCreate, traceLevel));
+ append(generate(rSuccCreate, traceLevel));
}
if (!rSuccUpdate.isEmpty()) {
report.append("\nRealms updated:\n").
- append(ProvisioningReport.generate(rSuccUpdate, traceLevel));
+ append(generate(rSuccUpdate, traceLevel));
}
if (!rSuccDelete.isEmpty()) {
report.append("\nRealms deleted:\n").
- append(ProvisioningReport.generate(rSuccDelete, traceLevel));
+ append(generate(rSuccDelete, traceLevel));
}
if (!rSuccNone.isEmpty()) {
report.append("\nRealms no operation:\n").
- append(ProvisioningReport.generate(rSuccNone, traceLevel));
+ append(generate(rSuccNone, traceLevel));
}
if (!rIgnore.isEmpty()) {
report.append("\nRealms ignored:\n").
- append(ProvisioningReport.generate(rIgnore, traceLevel));
+ append(generate(rIgnore, traceLevel));
}
}
}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
index 15ec686..9884a63 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
@@ -19,9 +19,9 @@
package org.apache.syncope.core.provisioning.java.pushpull;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Date;
import java.util.List;
+import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.syncope.common.lib.AnyOperations;
@@ -55,7 +55,7 @@ import org.apache.syncope.core.provisioning.api.cache.VirAttrCache;
import org.apache.syncope.core.provisioning.api.cache.VirAttrCacheValue;
import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException;
-import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
import org.apache.syncope.core.persistence.api.dao.PullMatch;
import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullExecutor;
@@ -188,7 +188,7 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
if (!profile.getTask().isPerformCreate()) {
LOG.debug("PullTask not configured for create");
end(provision.getAnyType().getKind(), UnmatchingRule.toEventName(rule), Result.SUCCESS, null, null, delta);
- return Collections.<ProvisioningReport>emptyList();
+ return List.of();
}
AnyCR anyCR = connObjectUtils.getAnyCR(delta.getObject(), profile.getTask(), provision, true);
@@ -263,7 +263,7 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
end(provision.getAnyType().getKind(), UnmatchingRule.toEventName(rule), resultStatus, null, output, delta);
}
- return Collections.singletonList(result);
+ return List.of(result);
}
protected void throwIgnoreProvisionException(final SyncDelta delta, final Exception exception)
@@ -293,7 +293,7 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
LOG.debug("PullTask not configured for update");
end(provision.getAnyType().getKind(),
MatchingRule.toEventName(MatchingRule.UPDATE), Result.SUCCESS, null, null, delta);
- return Collections.<ProvisioningReport>emptyList();
+ return List.of();
}
LOG.debug("About to update {}", matches);
@@ -402,7 +402,7 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
LOG.debug("PullTask not configured for update");
end(provision.getAnyType().getKind(),
MatchingRule.toEventName(matchingRule), Result.SUCCESS, null, null, delta);
- return Collections.<ProvisioningReport>emptyList();
+ return List.of();
}
LOG.debug("About to deprovision {}", matches);
@@ -519,7 +519,7 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
? MatchingRule.toEventName(MatchingRule.UNLINK)
: MatchingRule.toEventName(MatchingRule.LINK),
Result.SUCCESS, null, null, delta);
- return Collections.<ProvisioningReport>emptyList();
+ return List.of();
}
LOG.debug("About to update {}", matches);
@@ -619,7 +619,7 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
LOG.debug("PullTask not configured for delete");
end(provision.getAnyType().getKind(),
ResourceOperation.DELETE.name().toLowerCase(), Result.SUCCESS, null, null, delta);
- return Collections.<ProvisioningReport>emptyList();
+ return List.of();
}
LOG.debug("About to delete {}", matches);
@@ -649,7 +649,7 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
try {
getProvisioningManager().delete(
match.getAny().getKey(),
- Collections.singleton(profile.getTask().getResource().getKey()),
+ Set.of(profile.getTask().getResource().getKey()),
true);
output = null;
resultStatus = Result.SUCCESS;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
index f8a4e1b..50310f2 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
@@ -39,7 +39,7 @@ import org.apache.syncope.common.lib.types.ResourceOperation;
import org.apache.syncope.common.lib.types.UnmatchingRule;
import org.apache.syncope.core.persistence.api.entity.task.PushTask;
import org.apache.syncope.core.persistence.api.entity.user.User;
-import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
import org.apache.syncope.core.provisioning.api.pushpull.PushActions;
import org.apache.syncope.core.persistence.api.entity.Any;
import org.apache.syncope.core.persistence.api.entity.Entity;
@@ -88,13 +88,6 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
protected abstract String getName(Any<?> any);
- protected void reportPropagation(final ProvisioningReport result, final PropagationReporter reporter) {
- if (!reporter.getStatuses().isEmpty()) {
- result.setStatus(toProvisioningReportStatus(reporter.getStatuses().get(0).getStatus()));
- result.setMessage(reporter.getStatuses().get(0).getFailureReason());
- }
- }
-
protected void update(
final Any<?> any,
final Boolean enable,
@@ -494,7 +487,14 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
}
}
- protected ResourceOperation toResourceOperation(final UnmatchingRule rule) {
+ protected static void reportPropagation(final ProvisioningReport result, final PropagationReporter reporter) {
+ if (!reporter.getStatuses().isEmpty()) {
+ result.setStatus(toProvisioningReportStatus(reporter.getStatuses().get(0).getStatus()));
+ result.setMessage(reporter.getStatuses().get(0).getFailureReason());
+ }
+ }
+
+ protected static ResourceOperation toResourceOperation(final UnmatchingRule rule) {
switch (rule) {
case ASSIGN:
case PROVISION:
@@ -504,7 +504,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
}
}
- protected ResourceOperation toResourceOperation(final MatchingRule rule) {
+ protected static ResourceOperation toResourceOperation(final MatchingRule rule) {
switch (rule) {
case UPDATE:
return ResourceOperation.UPDATE;
@@ -516,7 +516,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
}
}
- protected ProvisioningReport.Status toProvisioningReportStatus(final ExecStatus status) {
+ protected static ProvisioningReport.Status toProvisioningReportStatus(final ExecStatus status) {
switch (status) {
case FAILURE:
return ProvisioningReport.Status.FAILURE;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DBPasswordPullActions.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DBPasswordPullActions.java
index fd7add0..9afbbb2 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DBPasswordPullActions.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DBPasswordPullActions.java
@@ -35,7 +35,7 @@ import org.apache.syncope.core.persistence.api.entity.ConnInstance;
import org.apache.syncope.core.persistence.api.entity.user.User;
import org.apache.syncope.core.provisioning.api.Connector;
import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
-import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
import org.identityconnectors.framework.common.objects.SyncDelta;
import org.quartz.JobExecutionException;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultAnyObjectPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultAnyObjectPullResultHandler.java
index a87fff4..14943a0 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultAnyObjectPullResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultAnyObjectPullResultHandler.java
@@ -37,7 +37,7 @@ import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager;
import org.apache.syncope.core.provisioning.api.ProvisioningManager;
import org.apache.syncope.core.provisioning.api.WorkflowResult;
-import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
import org.identityconnectors.framework.common.objects.SyncDelta;
import org.apache.syncope.core.provisioning.api.pushpull.AnyObjectPullResultHandler;
import org.springframework.beans.factory.annotation.Autowired;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultGroupPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultGroupPullResultHandler.java
index 96c765a..63ba2c0 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultGroupPullResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultGroupPullResultHandler.java
@@ -40,7 +40,7 @@ import org.apache.syncope.core.persistence.api.entity.group.Group;
import org.apache.syncope.core.provisioning.api.GroupProvisioningManager;
import org.apache.syncope.core.provisioning.api.ProvisioningManager;
import org.apache.syncope.core.provisioning.api.WorkflowResult;
-import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
import org.identityconnectors.framework.common.objects.SyncDelta;
import org.apache.syncope.core.provisioning.api.pushpull.GroupPullResultHandler;
import org.springframework.beans.factory.annotation.Autowired;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java
index f94a7d8..cd60747 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java
@@ -44,7 +44,7 @@ import org.apache.syncope.core.provisioning.api.PropagationByResource;
import org.apache.syncope.core.provisioning.api.propagation.PropagationException;
import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException;
-import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
import org.apache.syncope.core.provisioning.api.pushpull.RealmPullResultHandler;
import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullExecutor;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java
index a79d7e2..4d3bf03 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPushResultHandler.java
@@ -45,7 +45,7 @@ import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent;
import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException;
-import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
import org.apache.syncope.core.provisioning.api.pushpull.PushActions;
import org.apache.syncope.core.provisioning.api.pushpull.RealmPushResultHandler;
import org.apache.syncope.core.provisioning.java.job.AfterHandlingJob;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java
index 5e79274..facde19 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java
@@ -18,7 +18,6 @@
*/
package org.apache.syncope.core.provisioning.java.pushpull;
-import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
@@ -58,7 +57,7 @@ import org.apache.syncope.core.provisioning.api.ProvisioningManager;
import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
import org.apache.syncope.core.provisioning.api.WorkflowResult;
import org.apache.syncope.core.provisioning.api.propagation.PropagationException;
-import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
import org.apache.syncope.core.spring.security.AuthContextUtils;
import org.identityconnectors.framework.common.objects.SyncDelta;
@@ -368,7 +367,7 @@ public class DefaultUserPullResultHandler extends AbstractPullResultHandler impl
req,
report,
null,
- Collections.singleton(profile.getTask().getResource().getKey()),
+ Set.of(profile.getTask().getResource().getKey()),
true);
resultStatus = Result.SUCCESS;
@@ -493,7 +492,7 @@ public class DefaultUserPullResultHandler extends AbstractPullResultHandler impl
patch,
report,
null,
- Collections.singleton(profile.getTask().getResource().getKey()),
+ Set.of(profile.getTask().getResource().getKey()),
true);
resultStatus = Result.SUCCESS;
@@ -587,7 +586,7 @@ public class DefaultUserPullResultHandler extends AbstractPullResultHandler impl
req,
report,
null,
- Collections.singleton(profile.getTask().getResource().getKey()),
+ Set.of(profile.getTask().getResource().getKey()),
true);
resultStatus = Result.SUCCESS;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java
index a34bc6b..8f04fef 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java
@@ -45,7 +45,7 @@ import org.apache.syncope.core.provisioning.api.WorkflowResult;
import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException;
-import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
import org.apache.syncope.core.provisioning.api.pushpull.PushActions;
import org.apache.syncope.core.provisioning.api.pushpull.UserPushResultHandler;
import org.apache.syncope.core.provisioning.java.propagation.DefaultPropagationReporter;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java
index a7ec5f3..3646a76 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java
@@ -20,7 +20,6 @@ package org.apache.syncope.core.provisioning.java.pushpull;
import java.text.ParseException;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@@ -58,7 +57,7 @@ import org.apache.syncope.core.persistence.api.entity.PlainSchema;
import org.apache.syncope.core.persistence.api.entity.VirSchema;
import org.apache.syncope.core.persistence.api.entity.resource.Item;
import org.apache.syncope.core.provisioning.api.VirAttrHandler;
-import org.apache.syncope.core.provisioning.java.IntAttrNameParser;
+import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
import org.apache.syncope.core.spring.ImplementationManager;
import org.identityconnectors.framework.common.objects.Attribute;
@@ -212,13 +211,13 @@ public class InboundMatcher {
List<Object> output = transformer.beforePull(
connObjectKeyItem,
null,
- Collections.<Object>singletonList(finalConnObjectKeyValue));
+ List.of(finalConnObjectKeyValue));
if (!CollectionUtils.isEmpty(output)) {
finalConnObjectKeyValue = output.get(0).toString();
}
}
- List<PullMatch> noMatchResult = Collections.singletonList(PullCorrelationRule.NO_MATCH);
+ List<PullMatch> noMatchResult = List.of(PullCorrelationRule.NO_MATCH);
IntAttrName intAttrName;
try {
@@ -356,7 +355,7 @@ public class InboundMatcher {
}
}
- List<PullMatch> result = Collections.emptyList();
+ List<PullMatch> result = List.of();
try {
if (rule.isPresent()) {
result = matchByCorrelationRule(syncDelta, provision, rule.get(), provision.getAnyType().getKind());
@@ -376,7 +375,7 @@ public class InboundMatcher {
}
}
if (connObjectKeyValue == null) {
- result = Collections.singletonList(PullCorrelationRule.NO_MATCH);
+ result = List.of(PullCorrelationRule.NO_MATCH);
} else {
result = matchByConnObjectKeyValue(connObjectKeyItem.get(), connObjectKeyValue, provision);
}
@@ -412,14 +411,14 @@ public class InboundMatcher {
}
}
if (connObjectKey == null) {
- return Collections.emptyList();
+ return List.of();
}
for (ItemTransformer transformer : MappingUtils.getItemTransformers(connObjectKeyItem.get())) {
List<Object> output = transformer.beforePull(
connObjectKeyItem.get(),
null,
- Collections.<Object>singletonList(connObjectKey));
+ List.of(connObjectKey));
if (!CollectionUtils.isEmpty(output)) {
connObjectKey = output.get(0).toString();
}
@@ -459,4 +458,3 @@ public class InboundMatcher {
return result;
}
}
-
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActions.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActions.java
index 7b09175..22550fc 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActions.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActions.java
@@ -30,7 +30,7 @@ import org.apache.syncope.common.lib.to.GroupTO;
import org.apache.syncope.core.persistence.api.dao.GroupDAO;
import org.apache.syncope.core.provisioning.api.Connector;
import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
-import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
import org.apache.syncope.core.persistence.api.dao.PullMatch;
import org.apache.syncope.core.persistence.api.entity.resource.Provision;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActions.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActions.java
index 0c7df3f..cb4703c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActions.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPPasswordPullActions.java
@@ -35,7 +35,7 @@ import org.apache.syncope.common.lib.types.CipherAlgorithm;
import org.apache.syncope.core.persistence.api.dao.UserDAO;
import org.apache.syncope.core.persistence.api.entity.user.User;
import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
-import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
import org.identityconnectors.framework.common.objects.SyncDelta;
import org.quartz.JobExecutionException;
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 6e98595..41637c6 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
@@ -182,7 +182,7 @@ public class OutboundMatcher {
LOG.error("Could not match {} with any existing {}", any, provision.getObjectClass(), e);
}
- if (result.size() == 1) {
+ if (any != null && result.size() == 1) {
virAttrHandler.setValues(any, result.get(0));
}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
index 2638b96..726323c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
@@ -128,22 +128,22 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
}
}
- protected static RealmPushResultHandler buildRealmHandler() {
+ protected RealmPushResultHandler buildRealmHandler() {
return (RealmPushResultHandler) ApplicationContextProvider.getBeanFactory().
createBean(DefaultRealmPushResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
}
- protected static AnyObjectPushResultHandler buildAnyObjectHandler() {
+ protected AnyObjectPushResultHandler buildAnyObjectHandler() {
return (AnyObjectPushResultHandler) ApplicationContextProvider.getBeanFactory().
createBean(DefaultAnyObjectPushResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
}
- protected static UserPushResultHandler buildUserHandler() {
+ protected UserPushResultHandler buildUserHandler() {
return (UserPushResultHandler) ApplicationContextProvider.getBeanFactory().
createBean(DefaultUserPushResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
}
- protected static GroupPushResultHandler buildGroupHandler() {
+ protected GroupPushResultHandler buildGroupHandler() {
return (GroupPushResultHandler) ApplicationContextProvider.getBeanFactory().
createBean(DefaultGroupPushResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
}
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 02abde5..a31d08e 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
@@ -23,6 +23,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
import org.apache.syncope.common.lib.to.PullTaskTO;
import org.apache.syncope.common.lib.types.ClientExceptionType;
import org.apache.syncope.common.lib.types.ConflictResolutionAction;
@@ -42,14 +43,13 @@ import org.apache.syncope.core.persistence.api.entity.task.PullTask;
import org.apache.syncope.core.provisioning.api.Connector;
import org.apache.syncope.core.provisioning.api.pushpull.GroupPullResultHandler;
import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
-import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
import org.apache.syncope.core.provisioning.api.pushpull.ReconFilterBuilder;
import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullResultHandler;
import org.apache.syncope.core.provisioning.api.pushpull.SyncopeSinglePullExecutor;
import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
-import org.apache.syncope.core.spring.ImplementationManager;
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;
@@ -160,7 +160,7 @@ public class SinglePullJobDelegate extends PullJobDelegate implements SyncopeSin
connector.filteredReconciliation(
provision.getObjectClass(),
- new AccountReconciliationFilterBuilder(connObjectKey, connObjectValue),
+ new SingleReconciliationFilterBuilder(connObjectKey, connObjectValue),
handler,
MappingUtils.buildOperationOptions(mapItems, moreAttrsToGet.toArray(new String[0])));
@@ -182,13 +182,13 @@ public class SinglePullJobDelegate extends PullJobDelegate implements SyncopeSin
}
}
- static class AccountReconciliationFilterBuilder implements ReconFilterBuilder {
+ static class SingleReconciliationFilterBuilder implements ReconFilterBuilder {
private final String key;
private final String value;
- AccountReconciliationFilterBuilder(final String key, final String value) {
+ SingleReconciliationFilterBuilder(final String key, final String value) {
this.key = key;
this.value = value;
}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePushJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePushJobDelegate.java
index 4889311..f0c4258 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePushJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePushJobDelegate.java
@@ -33,7 +33,7 @@ import org.apache.syncope.core.persistence.api.entity.task.PushTask;
import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount;
import org.apache.syncope.core.provisioning.api.Connector;
import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
-import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
import org.apache.syncope.core.provisioning.api.pushpull.PushActions;
import org.apache.syncope.core.provisioning.api.pushpull.SyncopePushResultHandler;
import org.apache.syncope.core.provisioning.api.pushpull.SyncopeSinglePushExecutor;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamAnyObjectPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamAnyObjectPushResultHandler.java
new file mode 100644
index 0000000..17576cb
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamAnyObjectPushResultHandler.java
@@ -0,0 +1,73 @@
+/*
+ * 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.provisioning.java.pushpull.stream;
+
+import java.util.Set;
+import java.util.stream.Stream;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.resource.Item;
+import org.apache.syncope.core.persistence.api.entity.resource.Provision;
+import org.apache.syncope.core.provisioning.api.DerAttrHandler;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
+import org.apache.syncope.core.provisioning.java.propagation.DefaultPropagationReporter;
+import org.apache.syncope.core.provisioning.java.pushpull.DefaultAnyObjectPushResultHandler;
+import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class StreamAnyObjectPushResultHandler extends DefaultAnyObjectPushResultHandler {
+
+ @Autowired
+ private DerAttrHandler derAttrHandler;
+
+ private String executor;
+
+ public void setExecutor(final String executor) {
+ this.executor = executor;
+ }
+
+ @Override
+ protected void provision(final Any<?> any, final Boolean enabled, final ProvisioningReport result) {
+ Provision provision = profile.getTask().getResource().getProvisions().get(0);
+
+ Stream<? extends Item> items = MappingUtils.getPropagationItems(provision.getMapping().getItems().stream());
+
+ Pair<String, Set<Attribute>> preparedAttrs = mappingManager.prepareAttrs(any, null, false, enabled, provision);
+
+ PropagationTaskInfo propagationTask = propagationManager.newTask(
+ derAttrHandler,
+ any,
+ profile.getTask().getResource(),
+ ResourceOperation.CREATE,
+ provision,
+ false,
+ items,
+ preparedAttrs);
+ propagationTask.setConnector(profile.getConnector());
+ LOG.debug("PropagationTask created: {}", propagationTask);
+
+ PropagationReporter reporter = new DefaultPropagationReporter();
+ taskExecutor.execute(propagationTask, reporter, executor);
+ reportPropagation(result, reporter);
+ }
+}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamGroupPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamGroupPushResultHandler.java
new file mode 100644
index 0000000..32e6710
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamGroupPushResultHandler.java
@@ -0,0 +1,73 @@
+/*
+ * 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.provisioning.java.pushpull.stream;
+
+import java.util.Set;
+import java.util.stream.Stream;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.resource.Item;
+import org.apache.syncope.core.persistence.api.entity.resource.Provision;
+import org.apache.syncope.core.provisioning.api.DerAttrHandler;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
+import org.apache.syncope.core.provisioning.java.propagation.DefaultPropagationReporter;
+import org.apache.syncope.core.provisioning.java.pushpull.DefaultGroupPushResultHandler;
+import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class StreamGroupPushResultHandler extends DefaultGroupPushResultHandler {
+
+ @Autowired
+ private DerAttrHandler derAttrHandler;
+
+ private String executor;
+
+ public void setExecutor(final String executor) {
+ this.executor = executor;
+ }
+
+ @Override
+ protected void provision(final Any<?> any, final Boolean enabled, final ProvisioningReport result) {
+ Provision provision = profile.getTask().getResource().getProvisions().get(0);
+
+ Stream<? extends Item> items = MappingUtils.getPropagationItems(provision.getMapping().getItems().stream());
+
+ Pair<String, Set<Attribute>> preparedAttrs = mappingManager.prepareAttrs(any, null, false, enabled, provision);
+
+ PropagationTaskInfo propagationTask = propagationManager.newTask(
+ derAttrHandler,
+ any,
+ profile.getTask().getResource(),
+ ResourceOperation.CREATE,
+ provision,
+ false,
+ items,
+ preparedAttrs);
+ propagationTask.setConnector(profile.getConnector());
+ LOG.debug("PropagationTask created: {}", propagationTask);
+
+ PropagationReporter reporter = new DefaultPropagationReporter();
+ taskExecutor.execute(propagationTask, reporter, executor);
+ reportPropagation(result, reporter);
+ }
+}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java
new file mode 100644
index 0000000..ff6553e
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegate.java
@@ -0,0 +1,264 @@
+/*
+ * 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.provisioning.java.pushpull.stream;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Stream;
+import org.apache.syncope.common.lib.to.PullTaskTO;
+import org.apache.syncope.common.lib.types.ConflictResolutionAction;
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.syncope.common.lib.types.PullMode;
+import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.RealmDAO;
+import org.apache.syncope.core.persistence.api.entity.AnyType;
+import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.Implementation;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+import org.apache.syncope.core.persistence.api.entity.VirSchema;
+import org.apache.syncope.core.persistence.api.entity.policy.PullCorrelationRuleEntity;
+import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.resource.Item;
+import org.apache.syncope.core.persistence.api.entity.resource.Mapping;
+import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
+import org.apache.syncope.core.persistence.api.entity.resource.Provision;
+import org.apache.syncope.core.persistence.api.entity.task.PullTask;
+import org.apache.syncope.core.provisioning.api.pushpull.GroupPullResultHandler;
+import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
+import org.apache.syncope.common.lib.types.IdMImplementationType;
+import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
+import org.apache.syncope.core.provisioning.api.pushpull.stream.StreamConnector;
+import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullResultHandler;
+import org.apache.syncope.core.spring.ImplementationManager;
+import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.apache.syncope.core.provisioning.api.pushpull.stream.SyncopeStreamPullExecutor;
+import org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate;
+import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
+import org.apache.syncope.core.spring.security.SecureRandomUtils;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+
+@Component
+public class StreamPullJobDelegate extends PullJobDelegate implements SyncopeStreamPullExecutor {
+
+ @Autowired
+ private ImplementationDAO implementationDAO;
+
+ @Autowired
+ private RealmDAO realmDAO;
+
+ @Autowired
+ private PlainSchemaDAO plainSchemaDAO;
+
+ private PullPolicy pullPolicy(
+ final AnyType anyType,
+ final ConflictResolutionAction conflictResolutionAction,
+ final String pullCorrelationRule) {
+
+ PullCorrelationRuleEntity pullCorrelationRuleEntity = null;
+ if (pullCorrelationRule != null) {
+ Implementation impl = implementationDAO.find(pullCorrelationRule);
+ if (impl == null || !IdMImplementationType.PULL_ACTIONS.equals(impl.getType())) {
+ LOG.debug("Invalid " + Implementation.class.getSimpleName() + " {}, ignoring...", pullCorrelationRule);
+ } else {
+ pullCorrelationRuleEntity = entityFactory.newEntity(PullCorrelationRuleEntity.class);
+ pullCorrelationRuleEntity.setAnyType(anyType);
+ pullCorrelationRuleEntity.setImplementation(impl);
+ }
+ }
+
+ PullPolicy pullPolicy = entityFactory.newEntity(PullPolicy.class);
+ pullPolicy.setConflictResolutionAction(conflictResolutionAction);
+
+ if (pullCorrelationRuleEntity != null) {
+ pullPolicy.add(pullCorrelationRuleEntity);
+ pullCorrelationRuleEntity.setPullPolicy(pullPolicy);
+ }
+
+ return pullPolicy;
+ }
+
+ private Provision provision(
+ final AnyType anyType,
+ final String keyColumn,
+ final List<String> columns) throws JobExecutionException {
+
+ Provision provision = entityFactory.newEntity(Provision.class);
+ provision.setAnyType(anyType);
+ provision.setObjectClass(new ObjectClass(anyType.getKey()));
+
+ Mapping mapping = entityFactory.newEntity(Mapping.class);
+ provision.setMapping(mapping);
+ mapping.setProvision(provision);
+
+ AnyUtils anyUtils = anyUtilsFactory.getInstance(anyType.getKind());
+ if (anyUtils.getField(keyColumn) == null) {
+ PlainSchema keyColumnSchema = plainSchemaDAO.find(keyColumn);
+ if (keyColumnSchema == null) {
+ throw new JobExecutionException("Plain Schema for key column not found: " + keyColumn);
+ }
+ }
+
+ MappingItem connObjectKeyItem = entityFactory.newEntity(MappingItem.class);
+ connObjectKeyItem.setExtAttrName(keyColumn);
+ connObjectKeyItem.setIntAttrName(keyColumn);
+ connObjectKeyItem.setPurpose(MappingPurpose.PULL);
+ mapping.setConnObjectKeyItem(connObjectKeyItem);
+
+ columns.stream().
+ filter(column -> anyUtils.getField(column) != null
+ || plainSchemaDAO.find(column) != null || virSchemaDAO.find(column) != null).
+ map(column -> {
+ MappingItem item = entityFactory.newEntity(MappingItem.class);
+ item.setExtAttrName(column);
+ item.setIntAttrName(column);
+ item.setPurpose(MappingPurpose.PULL);
+ mapping.add(item);
+ return item;
+ }).forEach(mapping::add);
+
+ return provision;
+ }
+
+ private ExternalResource externalResource(
+ final AnyType anyType,
+ final String keyColumn,
+ final List<String> columns,
+ final ConflictResolutionAction conflictResolutionAction,
+ final String pullCorrelationRule) throws JobExecutionException {
+
+ Provision provision = provision(anyType, keyColumn, columns);
+
+ ExternalResource resource = entityFactory.newEntity(ExternalResource.class);
+ resource.setKey("StreamPull_" + SecureRandomUtils.generateRandomUUID().toString());
+ resource.add(provision);
+ provision.setResource(resource);
+
+ resource.setPullPolicy(pullPolicy(anyType, conflictResolutionAction, pullCorrelationRule));
+
+ return resource;
+ }
+
+ @Override
+ public List<ProvisioningReport> pull(
+ final AnyType anyType,
+ final String keyColumn,
+ final List<String> columns,
+ final ConflictResolutionAction conflictResolutionAction,
+ final String pullCorrelationRule,
+ final StreamConnector connector,
+ final PullTaskTO pullTaskTO) throws JobExecutionException {
+
+ LOG.debug("Executing stream pull");
+
+ List<PullActions> actions = new ArrayList<>();
+ pullTaskTO.getActions().forEach(key -> {
+ Implementation impl = implementationDAO.find(key);
+ if (impl == null || !IdMImplementationType.PULL_ACTIONS.equals(impl.getType())) {
+ LOG.debug("Invalid " + Implementation.class.getSimpleName() + " {}, ignoring...", key);
+ } else {
+ try {
+ actions.add(ImplementationManager.build(impl));
+ } catch (Exception e) {
+ LOG.warn("While building {}", impl, e);
+ }
+ }
+ });
+
+ try {
+ ExternalResource resource =
+ externalResource(anyType, keyColumn, columns, conflictResolutionAction, pullCorrelationRule);
+ Provision provision = resource.getProvisions().get(0);
+
+ PullTask pullTask = entityFactory.newEntity(PullTask.class);
+ pullTask.setResource(resource);
+ pullTask.setMatchingRule(pullTaskTO.getMatchingRule());
+ pullTask.setUnmatchingRule(pullTaskTO.getUnmatchingRule());
+ pullTask.setPullMode(PullMode.FULL_RECONCILIATION);
+ pullTask.setPerformCreate(true);
+ pullTask.setPerformUpdate(true);
+ pullTask.setPerformDelete(false);
+ pullTask.setSyncStatus(false);
+ pullTask.setDestinationRealm(realmDAO.findByFullPath(pullTaskTO.getDestinationRealm()));
+ pullTask.setRemediation(pullTaskTO.isRemediation());
+
+ profile = new ProvisioningProfile<>(connector, pullTask);
+ profile.setDryRun(false);
+ profile.setConflictResolutionAction(ConflictResolutionAction.FIRSTMATCH);
+ profile.getActions().addAll(actions);
+
+ for (PullActions action : actions) {
+ action.beforeAll(profile);
+ }
+
+ SyncopePullResultHandler handler;
+ GroupPullResultHandler ghandler = buildGroupHandler();
+ switch (anyType.getKind()) {
+ case USER:
+ handler = buildUserHandler();
+ break;
+
+ case GROUP:
+ handler = ghandler;
+ break;
+
+ case ANY_OBJECT:
+ default:
+ handler = buildAnyObjectHandler();
+ }
+ handler.setProfile(profile);
+ handler.setPullExecutor(this);
+
+ // execute filtered pull
+ Set<String> moreAttrsToGet = new HashSet<>();
+ actions.forEach(action -> moreAttrsToGet.addAll(action.moreAttrsToGet(profile, provision)));
+
+ Stream<? extends Item> mapItems = Stream.concat(
+ MappingUtils.getPullItems(provision.getMapping().getItems().stream()),
+ virSchemaDAO.findByProvision(provision).stream().map(VirSchema::asLinkingMappingItem));
+
+ connector.fullReconciliation(
+ provision.getObjectClass(),
+ handler,
+ MappingUtils.buildOperationOptions(mapItems, moreAttrsToGet.toArray(new String[0])));
+
+ try {
+ setGroupOwners(ghandler);
+ } catch (Exception e) {
+ LOG.error("While setting group owners", e);
+ }
+
+ for (PullActions action : actions) {
+ action.afterAll(profile);
+ }
+
+ return profile.getResults();
+ } catch (Exception e) {
+ throw e instanceof JobExecutionException
+ ? (JobExecutionException) e
+ : new JobExecutionException("While stream pulling", e);
+ }
+ }
+}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegate.java
new file mode 100644
index 0000000..1b775d4
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegate.java
@@ -0,0 +1,200 @@
+/*
+ * 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.provisioning.java.pushpull.stream;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
+import org.apache.syncope.common.lib.to.PushTaskTO;
+import org.apache.syncope.common.lib.types.ConflictResolutionAction;
+import org.apache.syncope.common.lib.types.IdMImplementationType;
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
+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.Implementation;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.resource.Mapping;
+import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
+import org.apache.syncope.core.persistence.api.entity.resource.Provision;
+import org.apache.syncope.core.persistence.api.entity.task.PushTask;
+import org.apache.syncope.core.provisioning.api.pushpull.AnyObjectPushResultHandler;
+import org.apache.syncope.core.provisioning.api.pushpull.GroupPushResultHandler;
+import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
+import org.apache.syncope.core.provisioning.api.pushpull.PushActions;
+import org.apache.syncope.core.provisioning.api.pushpull.stream.StreamConnector;
+import org.apache.syncope.core.provisioning.api.pushpull.SyncopePushResultHandler;
+import org.apache.syncope.core.provisioning.api.pushpull.stream.SyncopeStreamPushExecutor;
+import org.apache.syncope.core.provisioning.api.pushpull.UserPushResultHandler;
+import org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate;
+import org.apache.syncope.core.spring.ApplicationContextProvider;
+import org.apache.syncope.core.spring.ImplementationManager;
+import org.apache.syncope.core.spring.security.SecureRandomUtils;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.stereotype.Component;
+
+@Component
+public class StreamPushJobDelegate extends PushJobDelegate implements SyncopeStreamPushExecutor {
+
+ @Autowired
+ private ImplementationDAO implementationDAO;
+
+ private String executor;
+
+ @Override
+ protected AnyObjectPushResultHandler buildAnyObjectHandler() {
+ StreamAnyObjectPushResultHandler handler =
+ (StreamAnyObjectPushResultHandler) ApplicationContextProvider.getBeanFactory().createBean(
+ StreamAnyObjectPushResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+ handler.setExecutor(executor);
+ return handler;
+ }
+
+ @Override
+ protected UserPushResultHandler buildUserHandler() {
+ StreamUserPushResultHandler handler =
+ (StreamUserPushResultHandler) ApplicationContextProvider.getBeanFactory().
+ createBean(StreamUserPushResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+ handler.setExecutor(executor);
+ return handler;
+ }
+
+ @Override
+ protected GroupPushResultHandler buildGroupHandler() {
+ StreamGroupPushResultHandler handler = (StreamGroupPushResultHandler) ApplicationContextProvider.
+ getBeanFactory().
+ createBean(StreamGroupPushResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+ handler.setExecutor(executor);
+ return handler;
+ }
+
+ private ExternalResource externalResource(
+ final AnyType anyType, final List<String> columns) throws JobExecutionException {
+
+ Provision provision = entityFactory.newEntity(Provision.class);
+ provision.setAnyType(anyType);
+ provision.setObjectClass(new ObjectClass(anyType.getKey()));
+
+ Mapping mapping = entityFactory.newEntity(Mapping.class);
+ provision.setMapping(mapping);
+ mapping.setProvision(provision);
+
+ MappingItem connObjectKeyItem = entityFactory.newEntity(MappingItem.class);
+ connObjectKeyItem.setExtAttrName("key");
+ connObjectKeyItem.setIntAttrName("key");
+ connObjectKeyItem.setPurpose(MappingPurpose.NONE);
+ mapping.setConnObjectKeyItem(connObjectKeyItem);
+
+ columns.stream().map(column -> {
+ MappingItem item = entityFactory.newEntity(MappingItem.class);
+ item.setExtAttrName(column);
+ item.setIntAttrName(column);
+ item.setPurpose(MappingPurpose.PROPAGATION);
+ mapping.add(item);
+ return item;
+ }).forEach(mapping::add);
+
+ ExternalResource resource = entityFactory.newEntity(ExternalResource.class);
+ resource.setKey("StreamPush_" + SecureRandomUtils.generateRandomUUID().toString());
+ resource.add(provision);
+ provision.setResource(resource);
+
+ return resource;
+ }
+
+ @Override
+ public List<ProvisioningReport> push(
+ final AnyType anyType,
+ final List<? extends Any<?>> anys,
+ final List<String> columns,
+ final StreamConnector connector,
+ final PushTaskTO pushTaskTO,
+ final String executor) throws JobExecutionException {
+
+ LOG.debug("Executing stream push as {}", executor);
+ this.executor = executor;
+
+ List<PushActions> actions = new ArrayList<>();
+ pushTaskTO.getActions().forEach(key -> {
+ Implementation impl = implementationDAO.find(key);
+ if (impl == null || !IdMImplementationType.PUSH_ACTIONS.equals(impl.getType())) {
+ LOG.debug("Invalid " + Implementation.class.getSimpleName() + " {}, ignoring...", key);
+ } else {
+ try {
+ actions.add(ImplementationManager.build(impl));
+ } catch (Exception e) {
+ LOG.warn("While building {}", impl, e);
+ }
+ }
+ });
+
+ try {
+ ExternalResource resource = externalResource(anyType, columns);
+ Provision provision = resource.getProvisions().get(0);
+
+ PushTask pushTask = entityFactory.newEntity(PushTask.class);
+ pushTask.setResource(resource);
+ pushTask.setMatchingRule(pushTaskTO.getMatchingRule());
+ pushTask.setUnmatchingRule(pushTaskTO.getUnmatchingRule());
+ pushTask.setPerformCreate(true);
+ pushTask.setPerformUpdate(true);
+ pushTask.setPerformDelete(true);
+ pushTask.setSyncStatus(false);
+
+ profile = new ProvisioningProfile<>(connector, pushTask);
+ profile.getActions().addAll(actions);
+ profile.setConflictResolutionAction(ConflictResolutionAction.FIRSTMATCH);
+
+ for (PushActions action : actions) {
+ action.beforeAll(profile);
+ }
+
+ SyncopePushResultHandler handler;
+ switch (provision.getAnyType().getKind()) {
+ case USER:
+ handler = buildUserHandler();
+ break;
+
+ case GROUP:
+ handler = buildGroupHandler();
+ break;
+
+ case ANY_OBJECT:
+ default:
+ handler = buildAnyObjectHandler();
+ }
+ handler.setProfile(profile);
+
+ doHandle(anys, handler, provision.getResource());
+
+ for (PushActions action : actions) {
+ action.afterAll(profile);
+ }
+
+ return profile.getResults();
+ } catch (Exception e) {
+ throw e instanceof JobExecutionException
+ ? (JobExecutionException) e
+ : new JobExecutionException("While stream pushing", e);
+ }
+ }
+}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamUserPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamUserPushResultHandler.java
new file mode 100644
index 0000000..9b788a0
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamUserPushResultHandler.java
@@ -0,0 +1,73 @@
+/*
+ * 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.provisioning.java.pushpull.stream;
+
+import java.util.Set;
+import java.util.stream.Stream;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.resource.Item;
+import org.apache.syncope.core.persistence.api.entity.resource.Provision;
+import org.apache.syncope.core.provisioning.api.DerAttrHandler;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
+import org.apache.syncope.core.provisioning.java.propagation.DefaultPropagationReporter;
+import org.apache.syncope.core.provisioning.java.pushpull.DefaultUserPushResultHandler;
+import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class StreamUserPushResultHandler extends DefaultUserPushResultHandler {
+
+ @Autowired
+ private DerAttrHandler derAttrHandler;
+
+ private String executor;
+
+ public void setExecutor(final String executor) {
+ this.executor = executor;
+ }
+
+ @Override
+ protected void provision(final Any<?> any, final Boolean enabled, final ProvisioningReport result) {
+ Provision provision = profile.getTask().getResource().getProvisions().get(0);
+
+ Stream<? extends Item> items = MappingUtils.getPropagationItems(provision.getMapping().getItems().stream());
+
+ Pair<String, Set<Attribute>> preparedAttrs = mappingManager.prepareAttrs(any, null, false, enabled, provision);
+
+ PropagationTaskInfo propagationTask = propagationManager.newTask(
+ derAttrHandler,
+ any,
+ profile.getTask().getResource(),
+ ResourceOperation.CREATE,
+ provision,
+ false,
+ items,
+ preparedAttrs);
+ propagationTask.setConnector(profile.getConnector());
+ LOG.debug("PropagationTask created: {}", propagationTask);
+
+ PropagationReporter reporter = new DefaultPropagationReporter();
+ taskExecutor.execute(propagationTask, reporter, executor);
+ reportPropagation(result, reporter);
+ }
+}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java
index cdb8280..1f52585 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/MappingUtils.java
@@ -25,20 +25,14 @@ import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
-import org.apache.commons.jexl3.JexlContext;
-import org.apache.commons.jexl3.MapContext;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.types.MappingPurpose;
-import org.apache.syncope.core.persistence.api.entity.Any;
-import org.apache.syncope.core.persistence.api.entity.Realm;
import org.apache.syncope.core.persistence.api.entity.resource.Item;
import org.apache.syncope.core.persistence.api.entity.resource.Mapping;
import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
-import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
import org.apache.syncope.core.persistence.api.entity.resource.Provision;
import org.apache.syncope.core.provisioning.java.data.JEXLItemTransformerImpl;
-import org.apache.syncope.core.provisioning.java.jexl.JexlUtils;
import org.apache.syncope.core.spring.ApplicationContextProvider;
import org.identityconnectors.framework.common.objects.Name;
import org.identityconnectors.framework.common.objects.OperationOptions;
@@ -77,85 +71,6 @@ public final class MappingUtils {
item -> item.getPurpose() == MappingPurpose.PULL || item.getPurpose() == MappingPurpose.BOTH);
}
- private static Name getName(final String evalConnObjectLink, final String connObjectKey) {
- // If connObjectLink evaluates to an empty string, just use the provided connObjectKey as Name(),
- // otherwise evaluated connObjectLink expression is taken as Name().
- Name name;
- if (StringUtils.isBlank(evalConnObjectLink)) {
- // add connObjectKey as __NAME__ attribute ...
- LOG.debug("Add connObjectKey [{}] as {}", connObjectKey, Name.NAME);
- name = new Name(connObjectKey);
- } else {
- LOG.debug("Add connObjectLink [{}] as {}", evalConnObjectLink, Name.NAME);
- name = new Name(evalConnObjectLink);
-
- // connObjectKey not propagated: it will be used to set the value for __UID__ attribute
- LOG.debug("connObjectKey will be used just as {} attribute", Uid.NAME);
- }
-
- return name;
- }
-
- /**
- * Build __NAME__ for propagation.
- * First look if there is a defined connObjectLink for the given resource (and in
- * this case evaluate as JEXL); otherwise, take given connObjectKey.
- *
- * @param any given any object
- * @param provision external resource
- * @param connObjectKey connector object key
- * @return the value to be propagated as __NAME__
- */
- public static Name evaluateNAME(final Any<?> any, final Provision provision, final String connObjectKey) {
- if (StringUtils.isBlank(connObjectKey)) {
- // LOG error but avoid to throw exception: leave it to the external resource
- LOG.warn("Missing ConnObjectKey value for {}: ", provision.getResource());
- }
-
- // Evaluate connObjectKey expression
- String connObjectLink = provision == null || provision.getMapping() == null
- ? null
- : provision.getMapping().getConnObjectLink();
- String evalConnObjectLink = null;
- if (StringUtils.isNotBlank(connObjectLink)) {
- JexlContext jexlContext = new MapContext();
- JexlUtils.addFieldsToContext(any, jexlContext);
- JexlUtils.addPlainAttrsToContext(any.getPlainAttrs(), jexlContext);
- JexlUtils.addDerAttrsToContext(any, jexlContext);
- evalConnObjectLink = JexlUtils.evaluate(connObjectLink, jexlContext);
- }
-
- return getName(evalConnObjectLink, connObjectKey);
- }
-
- /**
- * Build __NAME__ for propagation.
- * First look if there is a defined connObjectLink for the given resource (and in
- * this case evaluate as JEXL); otherwise, take given connObjectKey.
- *
- * @param realm given any object
- * @param orgUnit external resource
- * @param connObjectKey connector object key
- * @return the value to be propagated as __NAME__
- */
- public static Name evaluateNAME(final Realm realm, final OrgUnit orgUnit, final String connObjectKey) {
- if (StringUtils.isBlank(connObjectKey)) {
- // LOG error but avoid to throw exception: leave it to the external resource
- LOG.warn("Missing ConnObjectKey value for {}: ", orgUnit.getResource());
- }
-
- // Evaluate connObjectKey expression
- String connObjectLink = Optional.ofNullable(orgUnit).map(OrgUnit::getConnObjectLink).orElse(null);
- String evalConnObjectLink = null;
- if (StringUtils.isNotBlank(connObjectLink)) {
- JexlContext jexlContext = new MapContext();
- JexlUtils.addFieldsToContext(realm, jexlContext);
- evalConnObjectLink = JexlUtils.evaluate(connObjectLink, jexlContext);
- }
-
- return getName(evalConnObjectLink, connObjectKey);
- }
-
public static List<ItemTransformer> getItemTransformers(final Item item) {
List<ItemTransformer> result = new ArrayList<>();
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/TemplateUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/TemplateUtils.java
index 8dcdd81..79a0491 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/TemplateUtils.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/TemplateUtils.java
@@ -34,12 +34,12 @@ import org.apache.syncope.common.lib.to.GroupTO;
import org.apache.syncope.common.lib.to.GroupableRelatableTO;
import org.apache.syncope.common.lib.to.UserTO;
import org.apache.syncope.common.lib.types.ClientExceptionType;
-import org.apache.syncope.core.provisioning.java.jexl.JexlUtils;
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.AnyTemplate;
import org.apache.syncope.core.persistence.api.entity.group.Group;
import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/AbstractTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/AbstractTest.java
index b120f93..3d9f9ba 100644
--- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/AbstractTest.java
+++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/AbstractTest.java
@@ -19,8 +19,13 @@
package org.apache.syncope.core.provisioning.java;
import javax.persistence.EntityManager;
+import org.apache.syncope.common.lib.types.AMEntitlement;
+import org.apache.syncope.common.lib.types.EntitlementsHolder;
+import org.apache.syncope.common.lib.types.IdMEntitlement;
+import org.apache.syncope.common.lib.types.IdRepoEntitlement;
import org.apache.syncope.core.spring.ApplicationContextProvider;
import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.junit.jupiter.api.BeforeAll;
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
@@ -37,4 +42,11 @@ public abstract class AbstractTest {
return entityManager;
}
+
+ @BeforeAll
+ public static void init() {
+ EntitlementsHolder.getInstance().addAll(IdRepoEntitlement.values());
+ EntitlementsHolder.getInstance().addAll(IdMEntitlement.values());
+ EntitlementsHolder.getInstance().addAll(AMEntitlement.values());
+ }
}
diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ResourceDataBinderTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderTest.java
similarity index 98%
rename from core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ResourceDataBinderTest.java
rename to core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderTest.java
index e2ab0da..bf71aa8 100644
--- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/ResourceDataBinderTest.java
+++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderTest.java
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.provisioning.java;
+package org.apache.syncope.core.provisioning.java.data;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -40,6 +40,7 @@ import org.apache.syncope.core.persistence.api.entity.PlainSchema;
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.data.ResourceDataBinder;
+import org.apache.syncope.core.provisioning.java.AbstractTest;
import org.apache.syncope.core.spring.security.SyncopeAuthenticationDetails;
import org.apache.syncope.core.spring.security.SyncopeGrantedAuthority;
import org.identityconnectors.framework.common.objects.ObjectClass;
diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/MailTemplateTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/jexl/MailTemplateTest.java
similarity index 96%
rename from core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/MailTemplateTest.java
rename to core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/jexl/MailTemplateTest.java
index 1efd0ea..60fb718 100644
--- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/MailTemplateTest.java
+++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/jexl/MailTemplateTest.java
@@ -16,7 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.provisioning.java;
+package org.apache.syncope.core.provisioning.java.jexl;
+
+import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -34,8 +36,8 @@ import org.apache.commons.lang3.SerializationUtils;
import org.apache.syncope.common.lib.Attr;
import org.apache.syncope.common.lib.to.MembershipTO;
import org.apache.syncope.common.lib.to.UserTO;
-import org.apache.syncope.core.provisioning.java.jexl.JexlUtils;
import org.apache.syncope.core.persistence.api.dao.MailTemplateDAO;
+import org.apache.syncope.core.provisioning.java.AbstractTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/MappingTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/jexl/MappingTest.java
similarity index 80%
rename from core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/MappingTest.java
rename to core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/jexl/MappingTest.java
index ea47539..862a866 100644
--- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/MappingTest.java
+++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/jexl/MappingTest.java
@@ -16,7 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.provisioning.java;
+package org.apache.syncope.core.provisioning.java.jexl;
+
+import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -31,9 +33,7 @@ import org.apache.syncope.core.persistence.api.entity.Realm;
import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
import org.apache.syncope.core.persistence.api.entity.resource.Provision;
import org.apache.syncope.core.persistence.api.entity.user.User;
-import org.apache.syncope.core.provisioning.java.jexl.JexlUtils;
-import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
-import org.identityconnectors.framework.common.objects.Name;
+import org.apache.syncope.core.provisioning.java.AbstractTest;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
@@ -66,14 +66,16 @@ public class MappingTest extends AbstractTest {
User user = userDAO.findByUsername("rossini");
assertNotNull(user);
- Name name = MappingUtils.evaluateNAME(user, provision, user.getUsername());
- assertEquals("uid=rossini,ou=people,o=isp", name.getNameValue());
+ JexlContext jexlContext = new MapContext();
+ JexlUtils.addFieldsToContext(user, jexlContext);
+ JexlUtils.addPlainAttrsToContext(user.getPlainAttrs(), jexlContext);
- provision.getMapping().setConnObjectLink(
- "'uid=' + username + realm.replaceAll('/', ',o=') + ',ou=people,o=isp'");
+ assertEquals(
+ "uid=rossini,ou=people,o=isp",
+ JexlUtils.evaluate(provision.getMapping().getConnObjectLink(), jexlContext));
- name = MappingUtils.evaluateNAME(user, provision, user.getUsername());
- assertEquals("uid=rossini,o=even,ou=people,o=isp", name.getNameValue());
+ String connObjectLink = "'uid=' + username + realm.replaceAll('/', ',o=') + ',ou=people,o=isp'";
+ assertEquals("uid=rossini,o=even,ou=people,o=isp", JexlUtils.evaluate(connObjectLink, jexlContext));
}
@Test
diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegateTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegateTest.java
new file mode 100644
index 0000000..dfa523c
--- /dev/null
+++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPullJobDelegateTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.provisioning.java.pushpull.stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.fasterxml.jackson.databind.MappingIterator;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.to.PullTaskTO;
+import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.ConflictResolutionAction;
+import org.apache.syncope.common.lib.types.MatchingRule;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.common.lib.types.UnmatchingRule;
+import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
+import org.apache.syncope.core.provisioning.api.pushpull.stream.StreamConnector;
+import org.apache.syncope.core.provisioning.api.pushpull.stream.SyncopeStreamPullExecutor;
+import org.apache.syncope.core.provisioning.java.AbstractTest;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.junit.jupiter.api.Test;
+import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+@Transactional("Master")
+public class StreamPullJobDelegateTest extends AbstractTest {
+
+ @Autowired
+ private SyncopeStreamPullExecutor streamPullExecutor;
+
+ @Autowired
+ private AnyTypeDAO anyTypeDAO;
+
+ @Autowired
+ private UserDAO userDAO;
+
+ @Test
+ public void pull() throws JobExecutionException, IOException {
+ PullTaskTO pullTask = new PullTaskTO();
+ pullTask.setDestinationRealm(SyncopeConstants.ROOT_REALM);
+ pullTask.setRemediation(false);
+ pullTask.setMatchingRule(MatchingRule.UPDATE);
+ pullTask.setUnmatchingRule(UnmatchingRule.PROVISION);
+
+ Map<String, String> user = new HashMap<>();
+ user.put("username", "donizetti");
+ user.put("email", "donizetti@apache.org");
+ user.put("surname", "Donizetti");
+ user.put("firstname", "Gaetano");
+ user.put("fullname", "Gaetano Donizetti");
+ user.put("userId", "donizetti@apache.org");
+ Iterator<Map<String, String>> backing = List.of(user).iterator();
+
+ @SuppressWarnings("unchecked")
+ MappingIterator<Map<String, String>> itor = mock(MappingIterator.class);
+ when(itor.hasNext()).thenAnswer(invocation -> backing.hasNext());
+ when(itor.next()).thenAnswer(invocation -> backing.next());
+
+ List<String> columns = user.keySet().stream().collect(Collectors.toList());
+
+ List<ProvisioningReport> results = AuthContextUtils.callAsAdmin(SyncopeConstants.MASTER_DOMAIN, () -> {
+ try {
+ return streamPullExecutor.pull(
+ anyTypeDAO.findUser(),
+ "username",
+ columns,
+ ConflictResolutionAction.IGNORE,
+ null,
+ new StreamConnector("username", null, itor, null),
+ pullTask);
+ } catch (JobExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ assertEquals(1, results.size());
+
+ assertEquals(AnyTypeKind.USER.name(), results.get(0).getAnyType());
+ assertNotNull(results.get(0).getKey());
+ assertEquals("donizetti", results.get(0).getName());
+ assertEquals("donizetti", results.get(0).getUidValue());
+ assertEquals(ResourceOperation.CREATE, results.get(0).getOperation());
+ assertEquals(ProvisioningReport.Status.SUCCESS, results.get(0).getStatus());
+
+ User donizetti = userDAO.find(results.get(0).getKey());
+ assertNotNull(donizetti);
+ assertEquals("donizetti", donizetti.getUsername());
+ assertEquals("Gaetano", donizetti.getPlainAttr("firstname").get().getValuesAsStrings().get(0));
+ }
+}
diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegateTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegateTest.java
new file mode 100644
index 0000000..2fcab3c
--- /dev/null
+++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/stream/StreamPushJobDelegateTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.provisioning.java.pushpull.stream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import com.fasterxml.jackson.databind.MappingIterator;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SequenceWriter;
+import java.io.IOException;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
+import org.apache.syncope.common.lib.to.PushTaskTO;
+import org.apache.syncope.common.lib.types.MatchingRule;
+import org.apache.syncope.common.lib.types.UnmatchingRule;
+import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.provisioning.api.pushpull.stream.StreamConnector;
+import org.apache.syncope.core.provisioning.api.pushpull.stream.SyncopeStreamPushExecutor;
+import org.apache.syncope.core.provisioning.java.AbstractTest;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+@Transactional("Master")
+public class StreamPushJobDelegateTest extends AbstractTest {
+
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ @Autowired
+ private SyncopeStreamPushExecutor streamPushExecutor;
+
+ @Autowired
+ private AnyTypeDAO anyTypeDAO;
+
+ @Autowired
+ private UserDAO userDAO;
+
+ @Test
+ public void push() throws IOException {
+ PipedInputStream in = new PipedInputStream();
+ PipedOutputStream os = new PipedOutputStream(in);
+
+ PushTaskTO pushTask = new PushTaskTO();
+ pushTask.setMatchingRule(MatchingRule.UPDATE);
+ pushTask.setUnmatchingRule(UnmatchingRule.PROVISION);
+
+ List<ProvisioningReport> results = AuthContextUtils.callAsAdmin(SyncopeConstants.MASTER_DOMAIN, () -> {
+ try (SequenceWriter writer = MAPPER.writer().forType(Map.class).writeValues(os)) {
+ writer.init(true);
+
+ return streamPushExecutor.push(
+ anyTypeDAO.findUser(),
+ userDAO.findAll(1, 100),
+ Arrays.asList("username", "firstname", "surname", "email", "status", "loginDate"),
+ new StreamConnector(null, null, null, writer),
+ pushTask,
+ "user");
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+ assertEquals(userDAO.count(), results.size());
+
+ MappingIterator<Map<String, String>> reader = MAPPER.readerFor(Map.class).readValues(in);
+
+ for (int i = 0; i < results.size() && reader.hasNext(); i++) {
+ Map<String, String> row = reader.next();
+
+ assertEquals(results.get(i).getName(), row.get("username"));
+ assertEquals(userDAO.findByUsername(row.get("username")).getStatus(), row.get("status"));
+
+ switch (row.get("username")) {
+ case "rossini":
+ assertNull(row.get("email"));
+ assertTrue(row.get("loginDate").contains(","));
+ break;
+
+ case "verdi":
+ assertEquals("verdi@syncope.org", row.get("email"));
+ assertNull(row.get("loginDate"));
+ break;
+
+ case "bellini":
+ assertNull(row.get("email"));
+ assertFalse(row.get("loginDate").contains(","));
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelUserProvisioningManager.java b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelUserProvisioningManager.java
index ef0b317..4b7f46e 100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelUserProvisioningManager.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/CamelUserProvisioningManager.java
@@ -31,10 +31,10 @@ import org.apache.syncope.common.lib.request.StatusR;
import org.apache.syncope.common.lib.request.UserCR;
import org.apache.syncope.common.lib.request.UserUR;
import org.apache.syncope.common.lib.to.PropagationStatus;
+import org.apache.syncope.common.lib.to.ProvisioningReport;
import org.apache.syncope.core.provisioning.api.PropagationByResource;
import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
import org.apache.syncope.core.provisioning.api.UserWorkflowResult;
-import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Propagation;
diff --git a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java
index 32d3395..587dfa5 100644
--- a/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java
+++ b/ext/oidcclient/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java
@@ -40,11 +40,11 @@ import org.apache.syncope.core.persistence.api.entity.OIDCProvider;
import org.apache.syncope.core.persistence.api.entity.OIDCProviderItem;
import org.apache.syncope.core.persistence.api.entity.user.User;
import org.apache.syncope.core.provisioning.api.IntAttrName;
+import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
import org.apache.syncope.core.provisioning.api.OIDCProviderActions;
import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
import org.apache.syncope.core.provisioning.api.data.ItemTransformer;
import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
-import org.apache.syncope.core.provisioning.java.IntAttrNameParser;
import org.apache.syncope.core.provisioning.java.pushpull.InboundMatcher;
import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
diff --git a/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCProviderDataBinderImpl.java b/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCProviderDataBinderImpl.java
index 7fd536e..a206cf9 100644
--- a/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCProviderDataBinderImpl.java
+++ b/ext/oidcclient/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCProviderDataBinderImpl.java
@@ -41,8 +41,8 @@ import org.apache.syncope.core.persistence.api.entity.OIDCProviderItem;
import org.apache.syncope.core.persistence.api.entity.OIDCUserTemplate;
import org.apache.syncope.core.provisioning.api.IntAttrName;
import org.apache.syncope.core.provisioning.api.data.OIDCProviderDataBinder;
-import org.apache.syncope.core.provisioning.java.IntAttrNameParser;
-import org.apache.syncope.core.provisioning.java.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
diff --git a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2UserManager.java b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2UserManager.java
index 1d33f34..1e7c335 100644
--- a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2UserManager.java
+++ b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2UserManager.java
@@ -42,11 +42,11 @@ import org.apache.syncope.core.persistence.api.dao.UserDAO;
import org.apache.syncope.core.persistence.api.entity.SAML2IdP;
import org.apache.syncope.core.persistence.api.entity.user.User;
import org.apache.syncope.core.provisioning.api.IntAttrName;
+import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
import org.apache.syncope.core.provisioning.api.SAML2IdPActions;
import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
import org.apache.syncope.core.provisioning.api.data.ItemTransformer;
import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
-import org.apache.syncope.core.provisioning.java.IntAttrNameParser;
import org.apache.syncope.core.provisioning.java.pushpull.InboundMatcher;
import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
diff --git a/ext/saml2sp/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2IdPDataBinderImpl.java b/ext/saml2sp/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2IdPDataBinderImpl.java
index 87b765c..992a8ce 100644
--- a/ext/saml2sp/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2IdPDataBinderImpl.java
+++ b/ext/saml2sp/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2IdPDataBinderImpl.java
@@ -42,8 +42,8 @@ import org.apache.syncope.core.persistence.api.entity.SAML2IdPItem;
import org.apache.syncope.core.persistence.api.entity.SAML2UserTemplate;
import org.apache.syncope.core.provisioning.api.IntAttrName;
import org.apache.syncope.core.provisioning.api.data.SAML2IdPDataBinder;
-import org.apache.syncope.core.provisioning.java.IntAttrNameParser;
-import org.apache.syncope.core.provisioning.java.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
+import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml
index c327f43..a7b532e 100644
--- a/fit/core-reference/pom.xml
+++ b/fit/core-reference/pom.xml
@@ -427,6 +427,12 @@ under the License.
</includes>
</testResource>
<testResource>
+ <directory>${basedir}/../../core/idm/logic/src/test/resources</directory>
+ <includes>
+ <include>test1.csv</include>
+ </includes>
+ </testResource>
+ <testResource>
<directory>${basedir}/../../core/rest-cxf/src/main/resources</directory>
<includes>
<include>errorMessages.properties</include>
@@ -532,6 +538,17 @@ under the License.
<build>
<plugins>
<plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <inherited>true</inherited>
+ <configuration>
+ <includes>
+ <include>**/org/apache/syncope/fit/core/*ITCase.java</include>
+ </includes>
+ </configuration>
+ </plugin>
+
+ <plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<configuration>
@@ -644,6 +661,17 @@ under the License.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <inherited>true</inherited>
+ <configuration>
+ <includes>
+ <include>**/org/apache/syncope/fit/core/*ITCase.java</include>
+ </includes>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<inherited>true</inherited>
<executions>
@@ -769,6 +797,17 @@ under the License.
<plugins>
<plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <inherited>true</inherited>
+ <configuration>
+ <includes>
+ <include>**/org/apache/syncope/fit/core/*ITCase.java</include>
+ </includes>
+ </configuration>
+ </plugin>
+
+ <plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<configuration>
@@ -882,6 +921,17 @@ under the License.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <inherited>true</inherited>
+ <configuration>
+ <includes>
+ <include>**/org/apache/syncope/fit/core/*ITCase.java</include>
+ </includes>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<inherited>true</inherited>
<executions>
@@ -1025,6 +1075,17 @@ under the License.
<plugins>
<plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <inherited>true</inherited>
+ <configuration>
+ <includes>
+ <include>**/org/apache/syncope/fit/core/*ITCase.java</include>
+ </includes>
+ </configuration>
+ </plugin>
+
+ <plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<configuration>
@@ -1114,6 +1175,17 @@ under the License.
<plugins>
<plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <inherited>true</inherited>
+ <configuration>
+ <includes>
+ <include>**/org/apache/syncope/fit/core/*ITCase.java</include>
+ </includes>
+ </configuration>
+ </plugin>
+
+ <plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<configuration>
@@ -1203,6 +1275,17 @@ under the License.
<plugins>
<plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <inherited>true</inherited>
+ <configuration>
+ <includes>
+ <include>**/org/apache/syncope/fit/core/*ITCase.java</include>
+ </includes>
+ </configuration>
+ </plugin>
+
+ <plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<configuration>
@@ -1299,6 +1382,17 @@ under the License.
<plugins>
<plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <inherited>true</inherited>
+ <configuration>
+ <includes>
+ <include>**/org/apache/syncope/fit/core/*ITCase.java</include>
+ </includes>
+ </configuration>
+ </plugin>
+
+ <plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<configuration>
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
index 4405fcc..3a3e537 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
@@ -69,6 +69,7 @@ import org.apache.syncope.common.lib.to.ItemTO;
import org.apache.syncope.common.lib.to.PullTaskTO;
import org.apache.syncope.common.lib.to.ExecTO;
import org.apache.syncope.common.lib.to.ImplementationTO;
+import org.apache.syncope.common.lib.to.PropagationTaskTO;
import org.apache.syncope.common.lib.to.ProvisioningResult;
import org.apache.syncope.common.lib.to.RemediationTO;
import org.apache.syncope.common.lib.to.UserTO;
@@ -1036,15 +1037,21 @@ public class PullTaskITCase extends AbstractTaskITCase {
// exec task: one user from CSV will match the user created above and template will be applied
execProvisioningTask(taskService, TaskType.PULL, task.getKey(), MAX_WAIT_SECONDS, false);
- // check that template was successfully applied...
- userTO = userService.read(userTO.getKey());
- assertEquals("virtualvalue", userTO.getVirAttr("virtualdata").get().getValues().get(0));
+ // check that template was successfully applied
+ // 1. propagation to db
+ PagedResult<PropagationTaskTO> tasks = taskService.search(new TaskQuery.Builder(TaskType.PROPAGATION).
+ anyTypeKind(AnyTypeKind.USER).entityKey(userTO.getKey()).resource(RESOURCE_NAME_DBVIRATTR).build());
+ assertFalse(tasks.getResult().isEmpty());
+ assertEquals(ExecStatus.SUCCESS.name(), tasks.getResult().get(0).getLatestExecStatus());
- // ...and that propagation to db succeeded
JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
String value = queryForObject(jdbcTemplate,
MAX_WAIT_SECONDS, "SELECT USERNAME FROM testpull WHERE ID=?", String.class, userTO.getKey());
assertEquals("virtualvalue", value);
+
+ // 2. virtual attribute
+ userTO = userService.read(userTO.getKey());
+ assertEquals("virtualvalue", userTO.getVirAttr("virtualdata").get().getValues().get(0));
}
@Test
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 1635f02..822bb5f 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
@@ -19,24 +19,46 @@
package org.apache.syncope.fit.core;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import com.fasterxml.jackson.databind.MappingIterator;
+import com.fasterxml.jackson.dataformat.csv.CsvMapper;
+import com.fasterxml.jackson.dataformat.csv.CsvSchema;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.Date;
import org.apache.syncope.common.lib.request.AnyObjectCR;
+import org.apache.syncope.common.lib.Attr;
+import java.util.List;
+import java.util.Map;
import java.util.UUID;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import org.apache.cxf.jaxrs.client.Client;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.syncope.client.lib.SyncopeClient;
import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.to.AnyObjectTO;
-import org.apache.syncope.common.lib.Attr;
+import org.apache.syncope.common.lib.to.PagedResult;
+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.to.UserTO;
import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.common.lib.types.MatchType;
+import org.apache.syncope.common.lib.types.ResourceOperation;
import org.apache.syncope.common.lib.types.UnmatchingRule;
+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;
+import org.apache.syncope.common.rest.api.beans.CSVPushSpec;
import org.apache.syncope.common.rest.api.beans.ReconQuery;
+import org.apache.syncope.common.rest.api.service.ReconciliationService;
import org.apache.syncope.fit.AbstractITCase;
import org.identityconnectors.framework.common.objects.OperationalAttributes;
import org.identityconnectors.framework.common.objects.Uid;
@@ -168,4 +190,106 @@ public class ReconciliationITCase extends AbstractITCase {
AnyObjectTO printer = anyObjectService.read(externalName);
assertNotNull(printer);
}
+
+ @Test
+ public void importCSV() {
+ ReconciliationService service = adminClient.getService(ReconciliationService.class);
+ Client client = WebClient.client(service);
+ client.type(RESTHeaders.TEXT_CSV);
+
+ CSVPullSpec spec = new CSVPullSpec.Builder(AnyTypeKind.USER.name(), "username").build();
+ InputStream csv = getClass().getResourceAsStream("/test1.csv");
+
+ List<ProvisioningReport> results = service.pull(spec, csv);
+ assertEquals(AnyTypeKind.USER.name(), results.get(0).getAnyType());
+ assertNotNull(results.get(0).getKey());
+ assertEquals("donizetti", results.get(0).getName());
+ assertEquals("donizetti", results.get(0).getUidValue());
+ assertEquals(ResourceOperation.CREATE, results.get(0).getOperation());
+ assertEquals(ProvisioningReport.Status.SUCCESS, results.get(0).getStatus());
+
+ UserTO donizetti = userService.read(results.get(0).getKey());
+ assertNotNull(donizetti);
+ assertEquals("Gaetano", donizetti.getPlainAttr("firstname").get().getValues().get(0));
+ assertEquals(1, donizetti.getPlainAttr("loginDate").get().getValues().size());
+
+ UserTO cimarosa = userService.read(results.get(1).getKey());
+ assertNotNull(cimarosa);
+ assertEquals("Domenico Cimarosa", cimarosa.getPlainAttr("fullname").get().getValues().get(0));
+ assertEquals(2, cimarosa.getPlainAttr("loginDate").get().getValues().size());
+ }
+
+ @Test
+ public void exportCSV() throws IOException {
+ ReconciliationService service = adminClient.getService(ReconciliationService.class);
+ Client client = WebClient.client(service);
+ client.accept(RESTHeaders.TEXT_CSV);
+
+ AnyQuery anyQuery = new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM).
+ fiql(SyncopeClient.getUserSearchConditionBuilder().is("username").equalTo("*ini").query()).
+ page(1).
+ size(1000).
+ orderBy("username ASC").
+ build();
+
+ CSVPushSpec spec = new CSVPushSpec.Builder(AnyTypeKind.USER.name()).
+ ignorePagination(true).
+ field("username").
+ field("status").
+ plainAttr("firstname").
+ plainAttr("surname").
+ plainAttr("email").
+ plainAttr("loginDate").
+ build();
+
+ Response response = service.push(anyQuery, spec);
+ assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
+ assertEquals(
+ "attachment; filename=" + SyncopeConstants.MASTER_DOMAIN + ".csv",
+ response.getHeaderString(HttpHeaders.CONTENT_DISPOSITION));
+
+ PagedResult<UserTO> users = userService.search(anyQuery);
+ assertNotNull(users);
+
+ CsvSchema.Builder builder = CsvSchema.builder().setUseHeader(true);
+ builder.addColumn("username");
+ builder.addColumn("status");
+ builder.addColumn("firstname");
+ builder.addColumn("surname");
+ builder.addColumn("email");
+ builder.addColumn("loginDate");
+ CsvSchema schema = builder.build();
+
+ MappingIterator<Map<String, String>> reader = new CsvMapper().readerFor(Map.class).with(schema).
+ readValues((InputStream) response.getEntity());
+
+ int rows = 0;
+ for (; reader.hasNext(); rows++) {
+ Map<String, String> row = reader.next();
+
+ assertEquals(users.getResult().get(rows).getUsername(), row.get("username"));
+ assertEquals(users.getResult().get(rows).getStatus(), row.get("status"));
+
+ switch (row.get("username")) {
+ case "rossini":
+ assertEquals(spec.getNullValue(), row.get("email"));
+ assertTrue(row.get("loginDate").contains(spec.getArrayElementSeparator()));
+ break;
+
+ case "verdi":
+ assertEquals("verdi@syncope.org", row.get("email"));
+ assertEquals(spec.getNullValue(), row.get("loginDate"));
+ break;
+
+ case "bellini":
+ assertEquals(spec.getNullValue(), row.get("email"));
+ assertFalse(row.get("loginDate").contains(spec.getArrayElementSeparator()));
+ break;
+
+ default:
+ break;
+ }
+ }
+ assertEquals(rows, users.getTotalCount());
+ }
}
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserITCase.java
index 95a44dd..f0a2e61 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserITCase.java
@@ -31,11 +31,12 @@ import java.io.IOException;
import java.security.AccessControlException;
import java.util.ArrayList;
import java.util.Date;
-import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
+import java.util.function.Function;
+import java.util.stream.Collectors;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@@ -858,8 +859,8 @@ public class UserITCase extends AbstractITCase {
private static void verifyAsyncResult(final List<PropagationStatus> statuses) {
assertEquals(3, statuses.size());
- Map<String, PropagationStatus> byResource = new HashMap<>(3);
- statuses.forEach(status -> byResource.put(status.getResource(), status));
+ Map<String, PropagationStatus> byResource = statuses.stream().collect(
+ Collectors.toMap(PropagationStatus::getResource, Function.identity()));
assertEquals(ExecStatus.SUCCESS, byResource.get(RESOURCE_NAME_LDAP).getStatus());
assertTrue(byResource.get(RESOURCE_NAME_TESTDB).getStatus() == ExecStatus.CREATED
|| byResource.get(RESOURCE_NAME_TESTDB).getStatus() == ExecStatus.SUCCESS);
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/VirAttrITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/VirAttrITCase.java
index 05b65c8..cf3dd58 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/VirAttrITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/VirAttrITCase.java
@@ -223,13 +223,13 @@ public class VirAttrITCase extends AbstractITCase {
// 3. update virtual attribute directly
JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
- String value = queryForObject(jdbcTemplate,
+ String value = queryForObject(jdbcTemplate,
MAX_WAIT_SECONDS, "SELECT USERNAME FROM testpull WHERE ID=?", String.class, actual.getKey());
assertEquals("virattrcache", value);
jdbcTemplate.update("UPDATE testpull set USERNAME='virattrcache2' WHERE ID=?", actual.getKey());
- value = queryForObject(jdbcTemplate,
+ value = queryForObject(jdbcTemplate,
MAX_WAIT_SECONDS, "SELECT USERNAME FROM testpull WHERE ID=?", String.class, actual.getKey());
assertEquals("virattrcache2", value);
@@ -380,14 +380,15 @@ public class VirAttrITCase extends AbstractITCase {
// 4. update value on external resource
// ----------------------------------------
JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
- String value = queryForObject(
- jdbcTemplate, MAX_WAIT_SECONDS, "SELECT USERNAME FROM testpull WHERE ID=?", String.class, userTO.getKey());
+ String value = queryForObject(jdbcTemplate,
+ MAX_WAIT_SECONDS, "SELECT USERNAME FROM testpull WHERE ID=?", String.class, userTO.
+ getKey());
assertEquals("virattrcache", value);
jdbcTemplate.update("UPDATE testpull set USERNAME='virattrcache2' WHERE ID=?", userTO.getKey());
- value = queryForObject(
- jdbcTemplate, MAX_WAIT_SECONDS, "SELECT USERNAME FROM testpull WHERE ID=?", String.class, userTO.getKey());
+ value = queryForObject(jdbcTemplate,
+ MAX_WAIT_SECONDS, "SELECT USERNAME FROM testpull WHERE ID=?", String.class, userTO.getKey());
assertEquals("virattrcache2", value);
// ----------------------------------------
diff --git a/pom.xml b/pom.xml
index 7903d41..ed2b67a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -452,6 +452,7 @@ under the License.
<h2.version>1.4.200</h2.version>
<junit.version>5.5.2</junit.version>
+ <mockito.version>3.2.4</mockito.version>
<conf.directory>${project.build.directory}/test-classes</conf.directory>
<bundles.directory>${project.build.directory}/bundles</bundles.directory>
@@ -973,6 +974,11 @@ under the License.
</exclusions>
</dependency>
<dependency>
+ <groupId>com.fasterxml.jackson.dataformat</groupId>
+ <artifactId>jackson-dataformat-csv</artifactId>
+ <version>${jackson.version}</version>
+ </dependency>
+ <dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-joda</artifactId>
<version>${jackson.version}</version>
@@ -1785,10 +1791,22 @@ under the License.
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
- <version>3.2.0</version>
+ <version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-junit-jupiter</artifactId>
+ <version>${mockito.version}</version>
+ <scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
@@ -2446,7 +2464,8 @@ under the License.
<link>http://fasterxml.github.io/jackson-databind/javadoc/2.10/</link>
<link>http://fasterxml.github.io/jackson-annotations/javadoc/2.10/</link>
<link>http://fasterxml.github.io/jackson-dataformat-xml/javadoc/2.10/</link>
- <link>http://fasterxml.github.io/jackson-dataformat-yaml/javadoc/2.9.pr1/</link>
+ <link>http://fasterxml.github.io/jackson-dataformats-text/javadoc/yaml/2.10/</link>
+ <link>http://fasterxml.github.io/jackson-dataformats-text/javadoc/csv/2.10/</link>
<link>http://fasterxml.github.io/jackson-datatype-joda/javadoc/2.10/</link>
<link>http://www.javadoc.io/doc/org.apache.camel/camel-core/3.0.0/</link>
<link>http://www.javadoc.io/doc/org.apache.camel/camel-spring/3.0.0/</link>