diff --git a/archetype/src/main/resources/archetype-resources/console/pom.xml b/archetype/src/main/resources/archetype-resources/console/pom.xml index ca0a693f0ed..ec9fddb8d2b 100644 --- a/archetype/src/main/resources/archetype-resources/console/pom.xml +++ b/archetype/src/main/resources/archetype-resources/console/pom.xml @@ -122,14 +122,7 @@ under the License. standalone - - - - org.springframework.boot - spring-boot-starter-undertow - - - + diff --git a/archetype/src/main/resources/archetype-resources/core/pom.xml b/archetype/src/main/resources/archetype-resources/core/pom.xml index 13e2b72dbce..64c1c384a05 100644 --- a/archetype/src/main/resources/archetype-resources/core/pom.xml +++ b/archetype/src/main/resources/archetype-resources/core/pom.xml @@ -169,14 +169,7 @@ under the License. standalone - - - - org.springframework.boot - spring-boot-starter-undertow - - - + diff --git a/archetype/src/main/resources/archetype-resources/enduser/pom.xml b/archetype/src/main/resources/archetype-resources/enduser/pom.xml index d9bf3513bbe..79b7f28b6dc 100644 --- a/archetype/src/main/resources/archetype-resources/enduser/pom.xml +++ b/archetype/src/main/resources/archetype-resources/enduser/pom.xml @@ -150,13 +150,6 @@ under the License. standalone - - - org.springframework.boot - spring-boot-starter-undertow - - - diff --git a/archetype/src/main/resources/archetype-resources/wa/pom.xml b/archetype/src/main/resources/archetype-resources/wa/pom.xml index f5c257d88ad..00507a95895 100644 --- a/archetype/src/main/resources/archetype-resources/wa/pom.xml +++ b/archetype/src/main/resources/archetype-resources/wa/pom.xml @@ -114,13 +114,6 @@ under the License. standalone - - - - org.springframework.boot - spring-boot-starter-undertow - - diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMImplementationInfoProvider.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMImplementationInfoProvider.java index e4949f000ec..ed6b564466f 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMImplementationInfoProvider.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMImplementationInfoProvider.java @@ -24,7 +24,7 @@ import java.util.stream.Collectors; import org.apache.syncope.client.console.init.ClassPathScanImplementationLookup; import org.apache.syncope.client.console.rest.ImplementationRestClient; -import org.apache.syncope.common.lib.policy.PullCorrelationRuleConf; +import org.apache.syncope.common.lib.policy.InboundCorrelationRuleConf; import org.apache.syncope.common.lib.policy.PushCorrelationRuleConf; import org.apache.syncope.common.lib.to.ImplementationTO; import org.apache.syncope.common.lib.types.IdMImplementationType; @@ -44,7 +44,7 @@ public IdMImplementationInfoProvider( @Override public ViewMode getViewMode(final ImplementationTO implementation) { - return IdMImplementationType.PULL_CORRELATION_RULE.equals(implementation.getType()) + return IdMImplementationType.INBOUND_CORRELATION_RULE.equals(implementation.getType()) || IdMImplementationType.PUSH_CORRELATION_RULE.equals(implementation.getType()) ? ViewMode.JSON_BODY : super.getViewMode(implementation); @@ -55,8 +55,8 @@ public List getClasses(final ImplementationTO implementation, final View List classes = new ArrayList<>(); if (viewMode == ViewMode.JSON_BODY && IdMImplementationType.values().containsKey(implementation.getType())) { switch (implementation.getType()) { - case IdMImplementationType.PULL_CORRELATION_RULE: - classes = lookup.getClasses(PullCorrelationRuleConf.class).stream(). + case IdMImplementationType.INBOUND_CORRELATION_RULE: + classes = lookup.getClasses(InboundCorrelationRuleConf.class).stream(). map(Class::getName).collect(Collectors.toList()); break; @@ -88,16 +88,16 @@ public String getGroovyTemplateClassName(final String implementationType) { templateClassName = "MyPropagationActions"; break; - case IdMImplementationType.PULL_ACTIONS: - templateClassName = "MyPullActions"; + case IdMImplementationType.INBOUND_ACTIONS: + templateClassName = "MyInboundActions"; break; case IdMImplementationType.PUSH_ACTIONS: templateClassName = "MyPushActions"; break; - case IdMImplementationType.PULL_CORRELATION_RULE: - templateClassName = "MyPullCorrelationRule"; + case IdMImplementationType.INBOUND_CORRELATION_RULE: + templateClassName = "MyInboundCorrelationRule"; break; case IdMImplementationType.PUSH_CORRELATION_RULE: @@ -119,8 +119,8 @@ public String getGroovyTemplateClassName(final String implementationType) { public Class getClass(final String implementationType, final String name) { Class clazz; switch (implementationType) { - case IdMImplementationType.PULL_CORRELATION_RULE: - clazz = lookup.getClasses(PullCorrelationRuleConf.class).stream(). + case IdMImplementationType.INBOUND_CORRELATION_RULE: + clazz = lookup.getClasses(InboundCorrelationRuleConf.class).stream(). filter(c -> c.getName().equals(name)).findFirst().orElse(null); break; @@ -151,14 +151,28 @@ protected List load() { } @Override - public IModel> getPullActions() { + public IModel> getLiveSyncDeltaMappers() { return new LoadableDetachableModel<>() { private static final long serialVersionUID = 5275935387613157437L; @Override protected List load() { - return implementationRestClient.list(IdMImplementationType.PULL_ACTIONS).stream(). + return implementationRestClient.list(IdMImplementationType.LIVE_SYNC_DELTA_MAPPER).stream(). + map(ImplementationTO::getKey).sorted().collect(Collectors.toList()); + } + }; + } + + @Override + public IModel> getInboundActions() { + return new LoadableDetachableModel<>() { + + private static final long serialVersionUID = 5275935387613157437L; + + @Override + protected List load() { + return implementationRestClient.list(IdMImplementationType.INBOUND_ACTIONS).stream(). map(ImplementationTO::getKey).sorted().collect(Collectors.toList()); } }; diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMPolicyTabProvider.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMPolicyTabProvider.java index 4a38f37ec0e..826cd451410 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMPolicyTabProvider.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/commons/IdMPolicyTabProvider.java @@ -20,8 +20,8 @@ import java.util.ArrayList; import java.util.List; +import org.apache.syncope.client.console.policies.InboundPolicyDirectoryPanel; import org.apache.syncope.client.console.policies.PropagationPolicyDirectoryPanel; -import org.apache.syncope.client.console.policies.PullPolicyDirectoryPanel; import org.apache.syncope.client.console.policies.PushPolicyDirectoryPanel; import org.apache.syncope.client.console.rest.PolicyRestClient; import org.apache.wicket.PageReference; @@ -54,13 +54,13 @@ public Panel getPanel(final String panelId) { } }); - tabs.add(new AbstractTab(new ResourceModel("policy.pull")) { + tabs.add(new AbstractTab(new ResourceModel("policy.inbound")) { private static final long serialVersionUID = -6815067322125799251L; @Override public Panel getPanel(final String panelId) { - return new PullPolicyDirectoryPanel(panelId, policyRestClient, pageRef); + return new InboundPolicyDirectoryPanel(panelId, policyRestClient, pageRef); } }); diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/init/IdMClassPathScanImplementationContributor.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/init/IdMClassPathScanImplementationContributor.java index 28188f97562..aca16ac2d2e 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/init/IdMClassPathScanImplementationContributor.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/init/IdMClassPathScanImplementationContributor.java @@ -19,7 +19,7 @@ package org.apache.syncope.client.console.init; import java.util.Optional; -import org.apache.syncope.common.lib.policy.PullCorrelationRuleConf; +import org.apache.syncope.common.lib.policy.InboundCorrelationRuleConf; import org.apache.syncope.common.lib.policy.PushCorrelationRuleConf; import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.core.type.filter.AssignableTypeFilter; @@ -30,14 +30,14 @@ public class IdMClassPathScanImplementationContributor implements ClassPathScanI @Override public void extend(final ClassPathScanningCandidateComponentProvider scanner) { - scanner.addIncludeFilter(new AssignableTypeFilter(PullCorrelationRuleConf.class)); + scanner.addIncludeFilter(new AssignableTypeFilter(InboundCorrelationRuleConf.class)); scanner.addIncludeFilter(new AssignableTypeFilter(PushCorrelationRuleConf.class)); } @Override public Optional getLabel(final Class clazz) { - if (PullCorrelationRuleConf.class.isAssignableFrom(clazz)) { - return Optional.of(PullCorrelationRuleConf.class.getName()); + if (InboundCorrelationRuleConf.class.isAssignableFrom(clazz)) { + return Optional.of(InboundCorrelationRuleConf.class.getName()); } if (PushCorrelationRuleConf.class.isAssignableFrom(clazz)) { return Optional.of(PushCorrelationRuleConf.class.getName()); diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ResourceDirectoryPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ResourceDirectoryPanel.java index 65331f4eab2..b9ede15c03b 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ResourceDirectoryPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/panels/ResourceDirectoryPanel.java @@ -35,6 +35,7 @@ import org.apache.syncope.client.console.rest.ConnectorRestClient; import org.apache.syncope.client.console.rest.ResourceRestClient; import org.apache.syncope.client.console.status.ResourceStatusModal; +import org.apache.syncope.client.console.tasks.LiveSyncTask; import org.apache.syncope.client.console.tasks.PropagationTasks; import org.apache.syncope.client.console.tasks.PullTasks; import org.apache.syncope.client.console.tasks.PushTasks; @@ -158,7 +159,7 @@ protected String paginatorRowsKey() { @Override protected List> getColumns() { - final List> columns = new ArrayList<>(); + List> columns = new ArrayList<>(); columns.add(new PropertyColumn<>( new ResourceModel("key"), "keySortParam", "key")); columns.add(new PropertyColumn<>( @@ -173,7 +174,8 @@ protected Collection getBatches() { @Override public ActionsPanel getActions(final IModel model) { - final ActionsPanel panel = super.getActions(model); + ActionsPanel panel = super.getActions(model); + String key = ((ResourceTO) model.getObject()).getKey(); panel.add(new ActionLink<>() { @@ -181,7 +183,7 @@ public ActionsPanel getActions(final IModel model) { @Override public void onClick(final AjaxRequestTarget target, final Serializable ignore) { - ResourceTO resource = restClient.read(((ResourceTO) model.getObject()).getKey()); + ResourceTO resource = restClient.read(key); ConnInstanceTO connInstance = connectorRestClient.read(resource.getConnector()); IModel model = new CompoundPropertyModel<>(resource); @@ -198,8 +200,8 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { modal.header(new Model<>(MessageFormat.format(getString("resource.edit"), model.getObject().getKey()))); modal.show(true); } - }, ActionLink.ActionType.EDIT, String.format("%s,%s", IdMEntitlement.RESOURCE_READ, - IdMEntitlement.RESOURCE_UPDATE)); + }, ActionLink.ActionType.EDIT, + String.format("%s,%s", IdMEntitlement.RESOURCE_READ, IdMEntitlement.RESOURCE_UPDATE)); panel.add(new ActionLink<>() { @@ -207,7 +209,7 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { @Override public void onClick(final AjaxRequestTarget target, final Serializable ignore) { - ResourceTO resource = restClient.read(((ResourceTO) model.getObject()).getKey()); + ResourceTO resource = restClient.read(key); ConnInstanceTO connInstance = connectorRestClient.read(resource.getConnector()); if (SyncopeConsoleSession.get(). @@ -228,8 +230,8 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { model.getObject().getKey()))); provisionModal.show(true); } - }, ActionLink.ActionType.MAPPING, String.format("%s,%s", IdMEntitlement.RESOURCE_READ, - IdMEntitlement.RESOURCE_UPDATE)); + }, ActionLink.ActionType.MAPPING, + String.format("%s,%s", IdMEntitlement.RESOURCE_READ, IdMEntitlement.RESOURCE_UPDATE)); panel.add(new ActionLink<>() { @@ -237,7 +239,7 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { @Override public void onClick(final AjaxRequestTarget target, final Serializable ignore) { - ResourceTO resource = restClient.read(((ResourceTO) model.getObject()).getKey()); + ResourceTO resource = restClient.read(key); target.add(propTaskModal.setContent(new ConnObjects(resource, pageRef))); propTaskModal.header(new StringResourceModel("resource.explore.list", Model.of(model.getObject()))); @@ -251,10 +253,8 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { @Override public void onClick(final AjaxRequestTarget target, final Serializable ignore) { - target.add(propTaskModal.setContent( - new PropagationTasks(propTaskModal, ((ResourceTO) model.getObject()).getKey(), pageRef))); - propTaskModal.header(new Model<>(MessageFormat.format(getString("task.propagation.list"), - ((ResourceTO) model.getObject()).getKey()))); + target.add(propTaskModal.setContent(new PropagationTasks(propTaskModal, key, pageRef))); + propTaskModal.header(new Model<>(MessageFormat.format(getString("task.propagation.list"), key))); propTaskModal.show(true); } }, ActionLink.ActionType.PROPAGATION_TASKS, IdRepoEntitlement.TASK_LIST); @@ -265,24 +265,32 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { @Override public void onClick(final AjaxRequestTarget target, final Serializable ignore) { - target.add(schedTaskModal.setContent(new PullTasks( - schedTaskModal, ((ResourceTO) model.getObject()).getKey(), pageRef))); - schedTaskModal.header(new Model<>( - MessageFormat.format(getString("task.pull.list"), ((ResourceTO) model.getObject()).getKey()))); + target.add(schedTaskModal.setContent(new PullTasks(schedTaskModal, key, pageRef))); + schedTaskModal.header(new Model<>(MessageFormat.format(getString("task.pull.list"), key))); schedTaskModal.show(true); } }, ActionLink.ActionType.PULL_TASKS, IdRepoEntitlement.TASK_LIST); + panel.add(new ActionLink<>() { + + private static final long serialVersionUID = -4699610013584898667L; + + @Override + public void onClick(final AjaxRequestTarget target, final Serializable ignore) { + target.add(schedTaskModal.setContent(new LiveSyncTask(schedTaskModal, key, pageRef))); + schedTaskModal.header(new Model<>(MessageFormat.format(getString("task.livesync"), key))); + schedTaskModal.show(true); + } + }, ActionLink.ActionType.LIVE_SYNC_TASK, IdRepoEntitlement.TASK_READ); + panel.add(new ActionLink<>() { private static final long serialVersionUID = 2042227976628604686L; @Override public void onClick(final AjaxRequestTarget target, final Serializable ignore) { - target.add(schedTaskModal.setContent(new PushTasks( - schedTaskModal, ((ResourceTO) model.getObject()).getKey(), pageRef))); - schedTaskModal.header(new Model<>( - MessageFormat.format(getString("task.push.list"), ((ResourceTO) model.getObject()).getKey()))); + target.add(schedTaskModal.setContent(new PushTasks(schedTaskModal, key, pageRef))); + schedTaskModal.header(new Model<>(MessageFormat.format(getString("task.push.list"), key))); schedTaskModal.show(true); } }, ActionLink.ActionType.PUSH_TASKS, IdRepoEntitlement.TASK_LIST); @@ -293,10 +301,9 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { @Override public void onClick(final AjaxRequestTarget target, final Serializable ignore) { - ResourceTO modelObject = restClient.read(((ResourceTO) model.getObject()).getKey()); + ResourceTO modelObject = restClient.read(key); target.add(propTaskModal.setContent(new ResourceStatusModal(pageRef, modelObject))); - propTaskModal.header(new Model<>(MessageFormat.format(getString("resource.reconciliation"), - ((ResourceTO) model.getObject()).getKey()))); + propTaskModal.header(new Model<>(MessageFormat.format(getString("resource.reconciliation"), key))); propTaskModal.show(true); } }, ActionLink.ActionType.RECONCILIATION_RESOURCE, IdRepoEntitlement.USER_UPDATE); @@ -307,7 +314,7 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { @Override public void onClick(final AjaxRequestTarget target, final Serializable ignore) { - ResourceTO modelObject = restClient.read(((ResourceTO) model.getObject()).getKey()); + ResourceTO modelObject = restClient.read(key); target.add(historyModal.setContent(new AuditHistoryModal<>( OpEvent.CategoryType.LOGIC, @@ -326,7 +333,7 @@ protected void restore(final String json, final AjaxRequestTarget target) { SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED)); } catch (Exception e) { - LOG.error("While restoring resource {}", ((ResourceTO) model.getObject()).getKey(), e); + LOG.error("While restoring resource {}", key, e); SyncopeConsoleSession.get().onException(e); } ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target); @@ -335,12 +342,12 @@ protected void restore(final String json, final AjaxRequestTarget target) { historyModal.header( new Model<>(MessageFormat.format(getString("resource.menu.history"), - ((ResourceTO) model.getObject()).getKey()))); + key))); historyModal.show(true); } - }, ActionLink.ActionType.VIEW_AUDIT_HISTORY, String.format("%s,%s", IdMEntitlement.RESOURCE_READ, - IdRepoEntitlement.AUDIT_LIST)); + }, ActionLink.ActionType.VIEW_AUDIT_HISTORY, + String.format("%s,%s", IdMEntitlement.RESOURCE_READ, IdRepoEntitlement.AUDIT_LIST)); panel.add(new ActionLink<>() { @@ -349,15 +356,11 @@ protected void restore(final String json, final AjaxRequestTarget target) { @Override public void onClick(final AjaxRequestTarget target, final Serializable ignore) { try { - ResourceTO resource = restClient.read(((ResourceTO) model.getObject()).getKey()); + ResourceTO resource = restClient.read(key); resource.setKey("Copy of " + resource.getKey()); // reset some resource objects keys - resource.getProvisions().forEach(provision -> { - if (provision.getMapping() != null) { - provision.getMapping().getLinkingItems().clear(); - } - provision.getVirSchemas().clear(); - }); + resource.getProvisions().stream().filter(p -> p.getMapping() != null). + forEach(p -> p.getVirSchemas().clear()); target.add(modal.setContent(new ResourceWizardBuilder( resource, restClient, connectorRestClient, pageRef). build(BaseModal.CONTENT_ID, AjaxWizard.Mode.CREATE))); @@ -365,7 +368,7 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { modal.header(new Model<>(MessageFormat.format(getString("resource.clone"), resource.getKey()))); modal.show(true); } catch (SyncopeClientException e) { - LOG.error("While cloning resource {}", ((ResourceTO) model.getObject()).getKey(), e); + LOG.error("While cloning resource {}", key, e); SyncopeConsoleSession.get().onException(e); } ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target); @@ -379,13 +382,11 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) { @Override public void onClick(final AjaxRequestTarget target, final Serializable ignore) { try { - restClient.delete(((ResourceTO) model.getObject()).getKey()); - target.appendJavaScript(String.format("jsPlumb.remove('%s');", - ((ResourceTO) model.getObject()).getKey())); + restClient.delete(key); SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED)); } catch (SyncopeClientException e) { - LOG.error("While deleting resource {}", ((ResourceTO) model.getObject()).getKey(), e); + LOG.error("While deleting resource {}", key, e); SyncopeConsoleSession.get().onException(e); } ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target); diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/policies/PullPolicyDirectoryPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/policies/InboundPolicyDirectoryPanel.java similarity index 77% rename from client/idm/console/src/main/java/org/apache/syncope/client/console/policies/PullPolicyDirectoryPanel.java rename to client/idm/console/src/main/java/org/apache/syncope/client/console/policies/InboundPolicyDirectoryPanel.java index f98b74655d9..66cdfeda335 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/policies/PullPolicyDirectoryPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/policies/InboundPolicyDirectoryPanel.java @@ -22,7 +22,7 @@ import org.apache.syncope.client.console.rest.PolicyRestClient; import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink; import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel; -import org.apache.syncope.common.lib.policy.PullPolicyTO; +import org.apache.syncope.common.lib.policy.InboundPolicyTO; import org.apache.syncope.common.lib.types.IdRepoEntitlement; import org.apache.syncope.common.lib.types.PolicyType; import org.apache.wicket.PageReference; @@ -37,39 +37,41 @@ /** * Pull policies page. */ -public class PullPolicyDirectoryPanel extends PolicyDirectoryPanel { +public class InboundPolicyDirectoryPanel extends PolicyDirectoryPanel { private static final long serialVersionUID = 4984337552918213290L; - public PullPolicyDirectoryPanel(final String id, final PolicyRestClient restClient, final PageReference pageRef) { - super(id, restClient, PolicyType.PULL, pageRef); + public InboundPolicyDirectoryPanel( + final String id, final PolicyRestClient restClient, final PageReference pageRef) { - this.addNewItemPanelBuilder( - new PolicyModalPanelBuilder<>(PolicyType.PULL, new PullPolicyTO(), modal, restClient, pageRef), true); + super(id, restClient, PolicyType.INBOUND, pageRef); + + this.addNewItemPanelBuilder(new PolicyModalPanelBuilder<>( + PolicyType.INBOUND, new InboundPolicyTO(), modal, restClient, pageRef), true); MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, RENDER, IdRepoEntitlement.POLICY_CREATE); initResultTable(); } @Override - protected void addCustomColumnFields(final List> columns) { + protected void addCustomColumnFields(final List> columns) { columns.add(new PropertyColumn<>(new StringResourceModel( "conflictResolutionAction", this), "conflictResolutionAction", "conflictResolutionAction")); } @Override - protected void addCustomActions(final ActionsPanel panel, final IModel model) { + protected void addCustomActions(final ActionsPanel panel, final IModel model) { panel.add(new ActionLink<>() { private static final long serialVersionUID = -3722207913631435501L; @Override - public void onClick(final AjaxRequestTarget target, final PullPolicyTO ignore) { + public void onClick(final AjaxRequestTarget target, final InboundPolicyTO ignore) { target.add(policySpecModal.setContent( new ProvisioningPolicyModalPanel(model.getObject(), policySpecModal, pageRef))); policySpecModal.header(new StringResourceModel( - "policy.rules", PullPolicyDirectoryPanel.this, Model.of(model.getObject()))); + "policy.rules", InboundPolicyDirectoryPanel.this, Model.of(model.getObject()))); MetaDataRoleAuthorizationStrategy.authorize( policySpecModal.getForm(), ENABLE, IdRepoEntitlement.POLICY_UPDATE); diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/policies/ProvisioningPolicyModalPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/policies/ProvisioningPolicyModalPanel.java index 77765d4bebd..c4f3fe0faca 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/policies/ProvisioningPolicyModalPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/policies/ProvisioningPolicyModalPanel.java @@ -43,10 +43,10 @@ import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel; import org.apache.syncope.client.ui.commons.pages.BaseWebPage; import org.apache.syncope.common.lib.policy.AbstractCorrelationRuleConf; -import org.apache.syncope.common.lib.policy.DefaultPullCorrelationRuleConf; +import org.apache.syncope.common.lib.policy.DefaultInboundCorrelationRuleConf; import org.apache.syncope.common.lib.policy.DefaultPushCorrelationRuleConf; +import org.apache.syncope.common.lib.policy.InboundPolicyTO; import org.apache.syncope.common.lib.policy.ProvisioningPolicyTO; -import org.apache.syncope.common.lib.policy.PullPolicyTO; import org.apache.syncope.common.lib.to.ImplementationTO; import org.apache.syncope.common.lib.to.SchemaTO; import org.apache.syncope.common.lib.types.AnyTypeKind; @@ -102,8 +102,8 @@ public ProvisioningPolicyModalPanel( @Override protected Map load() { - return implementationRestClient.list(policyTO instanceof PullPolicyTO - ? IdMImplementationType.PULL_CORRELATION_RULE + return implementationRestClient.list(policyTO instanceof InboundPolicyTO + ? IdMImplementationType.INBOUND_CORRELATION_RULE : IdMImplementationType.PUSH_CORRELATION_RULE).stream(). collect(Collectors.toMap(ImplementationTO::getKey, Function.identity())); } @@ -115,8 +115,8 @@ protected Map load() { private final List rules = policyTO.getCorrelationRules().keySet().stream(). map(anyType -> new CorrelationRule( - policyTO instanceof PullPolicyTO - ? DefaultPullCorrelationRuleConf.class + policyTO instanceof InboundPolicyTO + ? DefaultInboundCorrelationRuleConf.class : DefaultPushCorrelationRuleConf.class, anyType, implementations.getObject().get(policyTO.getCorrelationRules().get(anyType)))). @@ -140,8 +140,8 @@ public void setObject(final List object) { @Override protected CorrelationRule newModelObject() { - return new CorrelationRule(policyTO instanceof PullPolicyTO - ? DefaultPullCorrelationRuleConf.class + return new CorrelationRule(policyTO instanceof InboundPolicyTO + ? DefaultInboundCorrelationRuleConf.class : DefaultPushCorrelationRuleConf.class); } @@ -172,8 +172,8 @@ public void onSubmit(final AjaxRequestTarget target) { } } }); - policyRestClient.update(getItem() instanceof PullPolicyTO - ? PolicyType.PULL : PolicyType.PUSH, getItem()); + policyRestClient.update(getItem() instanceof InboundPolicyTO + ? PolicyType.INBOUND : PolicyType.PUSH, getItem()); SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED)); this.modal.close(target); @@ -216,8 +216,8 @@ protected class CorrelationRulePanel extends Panel { @Override public Boolean getObject() { AbstractCorrelationRuleConf conf = correlationRule.getObject().getDefaultRuleConf(); - return conf instanceof DefaultPullCorrelationRuleConf - ? DefaultPullCorrelationRuleConf.class.cast(conf).isOrSchemas() + return conf instanceof DefaultInboundCorrelationRuleConf + ? DefaultInboundCorrelationRuleConf.class.cast(conf).isOrSchemas() : conf instanceof DefaultPushCorrelationRuleConf ? DefaultPushCorrelationRuleConf.class.cast(conf).isOrSchemas() : false; @@ -226,8 +226,8 @@ public Boolean getObject() { @Override public void setObject(final Boolean object) { AbstractCorrelationRuleConf conf = correlationRule.getObject().getDefaultRuleConf(); - if (conf instanceof DefaultPullCorrelationRuleConf) { - DefaultPullCorrelationRuleConf.class.cast(conf).setOrSchemas(object); + if (conf instanceof DefaultInboundCorrelationRuleConf) { + DefaultInboundCorrelationRuleConf.class.cast(conf).setOrSchemas(object); } else if (conf instanceof DefaultPushCorrelationRuleConf) { DefaultPushCorrelationRuleConf.class.cast(conf).setOrSchemas(object); } @@ -244,8 +244,8 @@ public void setObject(final Boolean object) { private List schemas() { AbstractCorrelationRuleConf conf = correlationRule.getObject().getDefaultRuleConf(); - return conf instanceof DefaultPullCorrelationRuleConf - ? DefaultPullCorrelationRuleConf.class.cast(conf).getSchemas() + return conf instanceof DefaultInboundCorrelationRuleConf + ? DefaultInboundCorrelationRuleConf.class.cast(conf).getSchemas() : conf instanceof DefaultPushCorrelationRuleConf ? DefaultPushCorrelationRuleConf.class.cast(conf).getSchemas() : List.of(); diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorRestClient.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorRestClient.java index 99623f89354..037bf02ad5c 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorRestClient.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorRestClient.java @@ -24,7 +24,6 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -50,6 +49,10 @@ public class ConnectorRestClient extends BaseRestClient { private static final long serialVersionUID = -6870366819966266617L; + protected static List filterBlank(final List properties) { + return properties.stream().filter(obj -> obj != null && !obj.toString().isBlank()).toList(); + } + public List getAllConnectors() { List connectors = List.of(); try { @@ -61,7 +64,7 @@ public List getAllConnectors() { } public ConnInstanceTO create(final ConnInstanceTO connectorTO) { - List filteredConf = filterProperties(connectorTO.getConf()); + List filteredConf = filterBlank(connectorTO.getConf()); connectorTO.getConf().clear(); connectorTO.getConf().addAll(filteredConf); @@ -91,12 +94,12 @@ public List getExtAttrNames( final String adminRealm, final String objectClass, final String connectorKey, - final Collection conf) { + final Optional> conf) { ConnInstanceTO connInstanceTO = new ConnInstanceTO(); connInstanceTO.setAdminRealm(adminRealm); connInstanceTO.setKey(connectorKey); - connInstanceTO.getConf().addAll(conf); + conf.ifPresent(c -> connInstanceTO.getConf().addAll(c)); // SYNCOPE-156: use provided info to give schema names (and type!) by ObjectClass Optional connIdObjectClass = buildObjectClassInfo(connInstanceTO, false).stream(). @@ -114,22 +117,14 @@ public List getExtAttrNames( * @return ConnInstanceTO */ public ConnInstanceTO read(final String key) { - ConnInstanceTO connectorTO = null; - - try { - connectorTO = getService(ConnectorService.class). - read(key, SyncopeConsoleSession.get().getLocale().toString()); - } catch (SyncopeClientException e) { - LOG.error("While reading a connector", e); - } - - return connectorTO; + return getService(ConnectorService.class).read(key, SyncopeConsoleSession.get().getLocale().toString()); } public void update(final ConnInstanceTO connectorTO) { - List filteredConf = filterProperties(connectorTO.getConf()); + List filteredConf = filterBlank(connectorTO.getConf()); connectorTO.getConf().clear(); connectorTO.getConf().addAll(filteredConf); + getService(ConnectorService.class).update(connectorTO); } @@ -152,25 +147,6 @@ public List getAllBundles() { return bundles; } - protected List filterProperties(final Collection properties) { - List newProperties = new ArrayList<>(); - - properties.stream().map(property -> { - ConnConfProperty prop = new ConnConfProperty(); - prop.setSchema(property.getSchema()); - prop.setOverridable(property.isOverridable()); - final List parsed = new ArrayList<>(); - if (property.getValues() != null) { - property.getValues().stream(). - filter(obj -> (obj != null && !obj.toString().isEmpty())). - forEachOrdered(parsed::add); - } - prop.getValues().addAll(parsed); - return prop; - }).forEachOrdered(newProperties::add); - return newProperties; - } - public boolean check(final String coreAddress, final String domain, final String jwt, final String key) throws IOException { @@ -191,8 +167,8 @@ public boolean check(final String coreAddress, final String domain, final String public Pair check(final ConnInstanceTO connectorTO) { ConnInstanceTO toBeChecked = new ConnInstanceTO(); - BeanUtils.copyProperties(connectorTO, toBeChecked, new String[] { "configuration", "configurationMap" }); - toBeChecked.getConf().addAll(filterProperties(connectorTO.getConf())); + BeanUtils.copyProperties(connectorTO, toBeChecked); + toBeChecked.getConf().addAll(filterBlank(connectorTO.getConf())); boolean check = false; String errorMessage = null; diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java index 49be7e07331..f1cbbe48943 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/status/ReconTaskPanel.java @@ -75,13 +75,13 @@ public class ReconTaskPanel extends MultilevelPanel.SecondLevel { @SpringBean protected ReconciliationRestClient reconciliationRestClient; - protected final IModel> pullActions = new LoadableDetachableModel<>() { + protected final IModel> inboundActions = new LoadableDetachableModel<>() { private static final long serialVersionUID = 5275935387613157437L; @Override protected List load() { - return implementationRestClient.list(IdMImplementationType.PULL_ACTIONS).stream(). + return implementationRestClient.list(IdMImplementationType.INBOUND_ACTIONS).stream(). map(ImplementationTO::getKey).sorted().collect(Collectors.toList()); } }; @@ -167,7 +167,7 @@ protected Iterator getChoices(final String input) { build("actions", new PropertyModel<>(taskTO, "actions"), new ListModel<>(taskTO instanceof PushTaskTO - ? pushActions.getObject() : pullActions.getObject())); + ? pushActions.getObject() : inboundActions.getObject())); actions.setOutputMarkupId(true); form.add(actions); diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java index 8dcfe9ed94a..2ff8db53785 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyTogglePanel.java @@ -22,7 +22,6 @@ import de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal; import java.io.Serializable; import java.text.MessageFormat; -import java.util.Optional; import org.apache.syncope.client.console.SyncopeConsoleSession; import org.apache.syncope.client.console.audit.AuditHistoryModal; import org.apache.syncope.client.console.panels.ConnObjects; @@ -31,6 +30,7 @@ import org.apache.syncope.client.console.rest.ConnectorRestClient; import org.apache.syncope.client.console.rest.ResourceRestClient; import org.apache.syncope.client.console.status.ResourceStatusModal; +import org.apache.syncope.client.console.tasks.LiveSyncTask; import org.apache.syncope.client.console.tasks.PropagationTasks; import org.apache.syncope.client.console.tasks.PullTasks; import org.apache.syncope.client.console.tasks.PushTasks; @@ -138,22 +138,17 @@ public void toggleWithContent(final AjaxRequestTarget target, final TopologyNode }); switch (node.getKind()) { - case SYNCOPE: + case SYNCOPE -> container.addOrReplace(getSyncopeFragment(pageRef)); - break; - case CONNECTOR_SERVER: + case CONNECTOR_SERVER -> container.addOrReplace(getLocationFragment(node, pageRef)); - break; - case FS_PATH: + case FS_PATH -> container.addOrReplace(getLocationFragment(node, pageRef)); - break; - case CONNECTOR: + case CONNECTOR -> container.addOrReplace(getConnectorFragment(node, pageRef)); - break; - case RESOURCE: + case RESOURCE -> container.addOrReplace(getResourceFragment(node, pageRef)); - break; - default: + default -> container.addOrReplace(getEmptyFragment()); } @@ -164,13 +159,13 @@ public void toggleWithContent(final AjaxRequestTarget target, final TopologyNode @Override protected String getTargetKey(final Serializable modelObject) { - String key = super.getTargetKey(modelObject); - if (modelObject instanceof ResourceProvision) { - key = ((ResourceProvision) modelObject).getKey(); - } else if (modelObject instanceof TopologyNode) { - key = ((TopologyNode) modelObject).getKey(); + if (modelObject instanceof ResourceProvision resourceProvision) { + return resourceProvision.getKey(); } - return key; + if (modelObject instanceof TopologyNode topologyNode) { + return topologyNode.getKey(); + } + return super.getTargetKey(modelObject); } private Fragment getEmptyFragment() { @@ -545,6 +540,25 @@ public String getAjaxIndicatorMarkupId() { MetaDataRoleAuthorizationStrategy.authorize(pull, RENDER, IdRepoEntitlement.TASK_LIST); fragment.add(pull); + AjaxLink livesync = new IndicatingAjaxLink<>("livesync") { + + private static final long serialVersionUID = 3776750333491622263L; + + @Override + public void onClick(final AjaxRequestTarget target) { + target.add(schedTaskModal.setContent(new LiveSyncTask(schedTaskModal, node.getKey(), pageRef))); + schedTaskModal.header(new Model<>(MessageFormat.format(getString("task.livesync"), node.getKey()))); + schedTaskModal.show(true); + } + + @Override + public String getAjaxIndicatorMarkupId() { + return Constants.VEIL_INDICATOR_MARKUP_ID; + } + }; + MetaDataRoleAuthorizationStrategy.authorize(livesync, RENDER, IdRepoEntitlement.TASK_READ); + fragment.add(livesync); + AjaxLink push = new IndicatingAjaxLink<>("push") { private static final long serialVersionUID = 3776750333491622263L; @@ -612,7 +626,6 @@ public String getAjaxIndicatorMarkupId() { String.format("%s,%s", IdMEntitlement.RESOURCE_READ, IdRepoEntitlement.AUDIT_LIST)); fragment.add(history); - // [SYNCOPE-1161] - Option to clone a resource AjaxLink clone = new IndicatingAjaxLink<>("clone") { private static final long serialVersionUID = -7978723352517770644L; @@ -654,14 +667,11 @@ public void onClick(final AjaxRequestTarget target) { public void onEvent(final IEvent event) { super.onEvent(event); - if (event.getPayload() instanceof AjaxWizard.NewItemFinishEvent) { - AjaxWizard.NewItemFinishEvent item = AjaxWizard.NewItemFinishEvent.class.cast(event.getPayload()); - Serializable result = item.getResult(); - Optional target = item.getTarget(); - if (result instanceof ConnInstanceTO) { - // update Toggle Panel header - target.ifPresent(t -> setHeader(t, ConnInstanceTO.class.cast(result).getDisplayName())); - } + if (event.getPayload() instanceof AjaxWizard.NewItemFinishEvent item + && item.getResult() instanceof ConnInstanceTO connInstance) { + + // update Toggle Panel header + item.getTarget().ifPresent(t -> setHeader(t, connInstance.getDisplayName())); } } diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder.java index 7870fa55f35..6fcbc5fba65 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder.java @@ -240,24 +240,24 @@ public class PullTask extends WizardStep { private static final long serialVersionUID = -8954789648303078732L; - private final IModel> pullActions = new LoadableDetachableModel<>() { + private final IModel> inboundActions = new LoadableDetachableModel<>() { private static final long serialVersionUID = 4659376149825914247L; @Override protected List load() { - return implementationRestClient.list(IdMImplementationType.PULL_ACTIONS).stream(). + return implementationRestClient.list(IdMImplementationType.INBOUND_ACTIONS).stream(). map(ImplementationTO::getKey).sorted().collect(Collectors.toList()); } }; - private final IModel> pullCorrelationRules = new LoadableDetachableModel<>() { + private final IModel> inboundCorrelationRules = new LoadableDetachableModel<>() { private static final long serialVersionUID = 4659376149825914247L; @Override protected List load() { - return implementationRestClient.list(IdMImplementationType.PULL_CORRELATION_RULE).stream(). + return implementationRestClient.list(IdMImplementationType.INBOUND_CORRELATION_RULE).stream(). map(ImplementationTO::getKey).sorted().collect(Collectors.toList()); } }; @@ -278,9 +278,10 @@ public PullTask(final CSVPullSpec spec) { unmatchingRule.setChoices(List.of(UnmatchingRule.values())); add(unmatchingRule); - AjaxPalettePanel provisioningActions = - new AjaxPalettePanel.Builder().build("provisioningActions", - new PropertyModel<>(spec, "provisioningActions"), new ListModel<>(pullActions.getObject())); + AjaxPalettePanel provisioningActions = new AjaxPalettePanel.Builder().build( + "provisioningActions", + new PropertyModel<>(spec, "provisioningActions"), + new ListModel<>(inboundActions.getObject())); add(provisioningActions); AjaxDropDownChoicePanel conflictResolutionAction = new AjaxDropDownChoicePanel<>( @@ -290,11 +291,12 @@ public PullTask(final CSVPullSpec spec) { conflictResolutionAction.setRequired(true); add(conflictResolutionAction); - AjaxDropDownChoicePanel pullCorrelationRule = new AjaxDropDownChoicePanel<>( - "pullCorrelationRule", "pullCorrelationRule", new PropertyModel<>(spec, "pullCorrelationRule"), + AjaxDropDownChoicePanel inboundCorrelationRule = new AjaxDropDownChoicePanel<>( + "inboundCorrelationRule", "inboundCorrelationRule", + new PropertyModel<>(spec, "inboundCorrelationRule"), false); - pullCorrelationRule.setChoices(pullCorrelationRules); - add(pullCorrelationRule); + inboundCorrelationRule.setChoices(inboundCorrelationRules); + add(inboundCorrelationRule); } } } diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/CapabilitiesPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/CapabilitiesPanel.java index b861db70da0..6a3b85a31f1 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/CapabilitiesPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/CapabilitiesPanel.java @@ -22,14 +22,14 @@ import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel; import org.apache.syncope.common.lib.types.ConnectorCapability; import org.apache.wicket.markup.html.panel.Panel; -import org.apache.wicket.model.PropertyModel; +import org.apache.wicket.model.IModel; import org.apache.wicket.model.util.ListModel; public class CapabilitiesPanel extends Panel { private static final long serialVersionUID = -2025535531121434050L; - public CapabilitiesPanel(final PropertyModel> model) { + public CapabilitiesPanel(final IModel> model) { super("capabilities"); setOutputMarkupId(true); diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorConfPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorConfPanel.java index a9588bf7363..fa3a63278e9 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorConfPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ConnectorConfPanel.java @@ -43,10 +43,6 @@ public ConnectorConfPanel(final ConnInstanceTO connInstanceTO, final List load() { List properties = getConnProperties(ConnectorConfPanel.this.modelObject); ConnectorConfPanel.this.modelObject.getConf().clear(); - - // re-order properties - properties.sort((o1, o2) -> o1 == null ? -1 : o1.compareTo(o2)); - ConnectorConfPanel.this.modelObject.getConf().addAll(properties); return properties; } @@ -62,23 +58,20 @@ protected List load() { * @return configuration properties. */ @Override - protected final List getConnProperties(final ConnInstanceTO instance) { - return ConnectorWizardBuilder.getBundle(instance, bundles).getProperties().stream(). - map(key -> { - ConnConfProperty property = new ConnConfProperty(); - property.setSchema(key); + protected List getConnProperties(final ConnInstanceTO instance) { + return ConnectorWizardBuilder.getBundle(instance, bundles).getProperties().stream().map(key -> { + ConnConfProperty property = new ConnConfProperty(); + property.setSchema(key); - instance.getConf(key.getName()).ifPresent(conf -> { - if (conf.getValues() != null) { - property.getValues().addAll(conf.getValues()); - property.setOverridable(conf.isOverridable()); - } - }); + instance.getConf(key.getName()).ifPresent(conf -> { + property.getValues().addAll(conf.getValues()); + property.setOverridable(conf.isOverridable()); + }); - if (property.getValues().isEmpty() && !key.getDefaultValues().isEmpty()) { - property.getValues().addAll(key.getDefaultValues()); - } - return property; - }).collect(Collectors.toList()); + if (property.getValues().isEmpty() && !key.getDefaultValues().isEmpty()) { + property.getValues().addAll(key.getDefaultValues()); + } + return property; + }).collect(Collectors.toList()); } } diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceConnCapabilitiesPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceConnCapabilitiesPanel.java index e80b30f8281..be835d8f38a 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceConnCapabilitiesPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceConnCapabilitiesPanel.java @@ -18,7 +18,12 @@ */ package org.apache.syncope.client.console.wizards.resources; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; import java.util.Set; +import org.apache.commons.lang3.BooleanUtils; import org.apache.syncope.client.ui.commons.Constants; import org.apache.syncope.client.ui.commons.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior; import org.apache.syncope.client.ui.commons.markup.html.form.AjaxCheckBoxPanel; @@ -26,7 +31,7 @@ import org.apache.syncope.common.lib.types.ConnectorCapability; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.extensions.wizard.WizardStep; -import org.apache.wicket.model.PropertyModel; +import org.apache.wicket.model.IModel; import org.apache.wicket.model.ResourceModel; public class ResourceConnCapabilitiesPanel extends WizardStep { @@ -34,23 +39,58 @@ public class ResourceConnCapabilitiesPanel extends WizardStep { private static final long serialVersionUID = -114632577031611754L; public ResourceConnCapabilitiesPanel( - final ResourceTO resourceTO, final Set connectorCapabilities) { + final ResourceTO resourceTO, + final Set connectorCapabilities) { + super(); setOutputMarkupId(true); - if (!resourceTO.isOverrideCapabilities() && resourceTO.getCapabilitiesOverride().isEmpty()) { - resourceTO.getCapabilitiesOverride().addAll(connectorCapabilities); - } + CapabilitiesPanel connCapabilitiesPanel = new CapabilitiesPanel(new IModel>() { + + private static final long serialVersionUID = -3729760042701500963L; + + @Override + public List getObject() { + return resourceTO.getCapabilitiesOverride(). + map(co -> { + List object = new ArrayList<>(co); + if (co.isEmpty()) { + co.addAll(connectorCapabilities); + } + return object; + }). + orElse(List.of()); + } - final CapabilitiesPanel connCapabilitiesPanel = new CapabilitiesPanel( - new PropertyModel<>(resourceTO, "capabilitiesOverride")); - connCapabilitiesPanel.setEnabled(resourceTO.isOverrideCapabilities()); + @Override + public void setObject(final List object) { + resourceTO.setCapabilitiesOverride(Optional.of(new HashSet<>(object))); + } + }); + connCapabilitiesPanel.setEnabled(!resourceTO.getCapabilitiesOverride().isEmpty()); add(connCapabilitiesPanel); - final AjaxCheckBoxPanel overrideCapabilities = new AjaxCheckBoxPanel( + AjaxCheckBoxPanel overrideCapabilities = new AjaxCheckBoxPanel( "overrideCapabilities", new ResourceModel("overrideCapabilities", "overrideCapabilities").getObject(), - new PropertyModel<>(resourceTO, "overrideCapabilities")); + new IModel() { + + private static final long serialVersionUID = -7523036477975507287L; + + @Override + public Boolean getObject() { + return !resourceTO.getCapabilitiesOverride().isEmpty(); + } + + @Override + public void setObject(final Boolean object) { + if (BooleanUtils.isTrue(object)) { + resourceTO.setCapabilitiesOverride(Optional.of(new HashSet<>())); + } else { + resourceTO.setCapabilitiesOverride(Optional.empty()); + } + } + }); overrideCapabilities.getField().add(new IndicatorAjaxFormComponentUpdatingBehavior(Constants.ON_CHANGE) { private static final long serialVersionUID = -1107858522700306810L; @@ -58,10 +98,14 @@ public ResourceConnCapabilitiesPanel( @Override protected void onUpdate(final AjaxRequestTarget target) { connCapabilitiesPanel.setEnabled(overrideCapabilities.getField().getModelObject()); + if (overrideCapabilities.getField().getModelObject()) { + resourceTO.setCapabilitiesOverride(Optional.of(connectorCapabilities)); + } else { + resourceTO.setCapabilitiesOverride(Optional.empty()); + } target.add(ResourceConnCapabilitiesPanel.this); } }); add(overrideCapabilities); } - } diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceConnConfPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceConnConfPanel.java index c86859c757b..9e988fea25b 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceConnConfPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceConnConfPanel.java @@ -22,12 +22,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import org.apache.syncope.client.console.rest.ConnectorRestClient; import org.apache.syncope.common.lib.to.ResourceTO; import org.apache.syncope.common.lib.types.ConnConfProperty; import org.apache.wicket.ajax.markup.html.form.AjaxButton; import org.apache.wicket.model.LoadableDetachableModel; -import org.apache.wicket.model.PropertyModel; import org.apache.wicket.spring.injection.annot.SpringBean; public abstract class ResourceConnConfPanel extends AbstractConnConfPanel { @@ -47,29 +47,8 @@ public ResourceConnConfPanel(final ResourceTO resourceTO) { @Override protected List load() { List confOverride = getConnProperties(resourceTO); - resourceTO.getConfOverride().clear(); - resourceTO.getConfOverride().addAll(confOverride); - - return new PropertyModel>(modelObject, "confOverride") { - - private static final long serialVersionUID = -7809699384012595307L; - - @Override - public List getObject() { - List res = new ArrayList<>(super.getObject()); - - // re-order properties - res.sort((left, right) -> { - if (left == null) { - return -1; - } else { - return left.compareTo(right); - } - }); - - return res; - } - }.getObject(); + resourceTO.setConfOverride(Optional.of(confOverride)); + return confOverride; } }; @@ -86,7 +65,7 @@ public List getObject() { * @return overridable properties. */ @Override - protected final List getConnProperties(final ResourceTO resourceTO) { + protected List getConnProperties(final ResourceTO resourceTO) { List props = new ArrayList<>(); if (resourceTO.getConnector() != null) { @@ -94,26 +73,21 @@ protected final List getConnProperties(final ResourceTO resour filter(ConnConfProperty::isOverridable). forEachOrdered(props::add); } - if (resourceTO.getConfOverride().isEmpty()) { - resourceTO.getConfOverride().clear(); - } else { + + resourceTO.getConfOverride().ifPresent(confOverride -> { Map valuedProps = new HashMap<>(); - resourceTO.getConfOverride().forEach(prop -> valuedProps.put(prop.getSchema().getName(), prop)); + confOverride.forEach(prop -> valuedProps.put(prop.getSchema().getName(), prop)); for (int i = 0; i < props.size(); i++) { if (valuedProps.containsKey(props.get(i).getSchema().getName())) { props.set(i, valuedProps.get(props.get(i).getSchema().getName())); } } - } + }); return props; } - public LoadableDetachableModel> getModel() { - return model; - } - public AjaxButton getCheck() { return check; } diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel.java index f1981be7543..e8c45f0229b 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel.java @@ -83,7 +83,7 @@ protected Map load() { @Override protected Map load() { - return policyRestClient.list(PolicyType.PULL).stream(). + return policyRestClient.list(PolicyType.INBOUND).stream(). collect(Collectors.toMap(PolicyTO::getKey, PolicyTO::getName, (v1, v2) -> v1, LinkedHashMap::new)); } }; @@ -153,15 +153,15 @@ public ResourceSecurityPanel(final ResourceTO resourceTO) { // ------------------------------- // Pull policy selection // ------------------------------- - AjaxDropDownChoicePanel pullPolicy = new AjaxDropDownChoicePanel<>( - "pullPolicy", - new ResourceModel("pullPolicy", "pullPolicy").getObject(), - new PropertyModel<>(resourceTO, "pullPolicy"), + AjaxDropDownChoicePanel inboundPolicy = new AjaxDropDownChoicePanel<>( + "inboundPolicy", + new ResourceModel("inboundPolicy", "inboundPolicy").getObject(), + new PropertyModel<>(resourceTO, "inboundPolicy"), false); - pullPolicy.setChoiceRenderer(new PolicyRenderer(pullPolicies.getObject())); - pullPolicy.setChoices(new ArrayList<>(pullPolicies.getObject().keySet())); - ((DropDownChoice) pullPolicy.getField()).setNullValid(true); - container.add(pullPolicy); + inboundPolicy.setChoiceRenderer(new PolicyRenderer(pullPolicies.getObject())); + inboundPolicy.setChoices(new ArrayList<>(pullPolicies.getObject().keySet())); + ((DropDownChoice) inboundPolicy.getField()).setNullValid(true); + container.add(inboundPolicy); // ------------------------------- // ------------------------------- diff --git a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceWizardBuilder.java b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceWizardBuilder.java index cfc8493e101..d43d9f6050f 100644 --- a/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceWizardBuilder.java +++ b/client/idm/console/src/main/java/org/apache/syncope/client/console/wizards/resources/ResourceWizardBuilder.java @@ -19,9 +19,9 @@ package org.apache.syncope.client.console.wizards.resources; import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; +import java.util.Optional; +import java.util.Set; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.client.console.rest.ConnectorRestClient; import org.apache.syncope.client.console.rest.ResourceRestClient; @@ -36,7 +36,6 @@ import org.apache.wicket.extensions.wizard.WizardModel; import org.apache.wicket.markup.ComponentTag; import org.apache.wicket.model.LoadableDetachableModel; -import org.apache.wicket.model.PropertyModel; /** * Resource wizard builder. @@ -105,31 +104,9 @@ protected void onUpdate(final AjaxRequestTarget target) { @Override protected List load() { - List confOverride = - resourceConnConfPanel.getConnProperties(resourceTO); - resourceTO.getConfOverride().clear(); - resourceTO.getConfOverride().addAll(confOverride); - - return new PropertyModel>(modelObject, "confOverride") { - - private static final long serialVersionUID = -7809699384012595307L; - - @Override - public List getObject() { - List res = new ArrayList<>(super.getObject()); - - // re-order properties - Collections.sort(res, (left, right) -> { - if (left == null) { - return -1; - } else { - return left.compareTo(right); - } - }); - - return res; - } - }.getObject(); + List confOverride = resourceConnConfPanel.getConnProperties(resourceTO); + resourceTO.setConfOverride(Optional.of(confOverride)); + return confOverride; } }; resourceConnConfPanel.setConfPropertyListView(model, true); @@ -139,12 +116,10 @@ public List getObject() { } wizardModel.add(resourceDetailsPanel); wizardModel.add(resourceConnConfPanel); - if (resourceTO.getConnector() != null) { - wizardModel.add(new ResourceConnCapabilitiesPanel( - resourceTO, connectorRestClient.read(resourceTO.getConnector()).getCapabilities())); - } else { - wizardModel.add(new ResourceConnCapabilitiesPanel(resourceTO, Collections.emptySet())); - } + Optional.ofNullable(resourceTO.getConnector()).ifPresentOrElse( + conn -> wizardModel.add(new ResourceConnCapabilitiesPanel( + resourceTO, connectorRestClient.read(conn).getCapabilities())), + () -> wizardModel.add(new ResourceConnCapabilitiesPanel(resourceTO, Set.of()))); wizardModel.add(new ResourceSecurityPanel(resourceTO)); return wizardModel; diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.html b/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.html index 64be1d7e3bd..5f654de365d 100644 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.html +++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.html @@ -63,6 +63,9 @@
  • + +
  • +
  • diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.properties index 6ade698bec6..c6d63eec6e7 100644 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.properties +++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel.properties @@ -41,3 +41,5 @@ resource.menu.reconciliation=Reconciliation resource.menu.push.list=Push tasks resource.menu.pull.list=Pull tasks resource.menu.propagation.list=Propagation tasks +resource.menu.livesync=Live Sync task +task.livesync=Live Sync task {0} diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_fr_CA.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_fr_CA.properties index 74874f3e28e..cffa797da47 100644 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_fr_CA.properties +++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_fr_CA.properties @@ -41,3 +41,5 @@ resource.menu.reconciliation=Rapprochement resource.menu.push.list=T\u00e2ches de pouss\u00e9e resource.menu.pull.list=T\u00e2ches d'extraction resource.menu.propagation.list=T\u00e2ches de propagation +resource.menu.livesync=Live Sync task +task.livesync=Live Sync task {0} diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_it.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_it.properties index 4f89a372ee8..8126fd268f8 100644 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_it.properties +++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_it.properties @@ -41,3 +41,5 @@ resource.menu.reconciliation=Riconciliazione resource.menu.push.list=Task di Push resource.menu.pull.list=Task di Pull resource.menu.propagation.list=Task di Propagazione +resource.menu.livesync=Task di Live Sync +task.livesync=Task di Live Sync {0} diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_ja.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_ja.properties index befad7fc371..6f61e6ea4e4 100644 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_ja.properties +++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_ja.properties @@ -41,3 +41,5 @@ resource.menu.reconciliation=\u7167\u5408 resource.menu.push.list=\u30d7\u30c3\u30b7\u30e5\u30bf\u30b9\u30af resource.menu.pull.list=\u30d7\u30eb\u30bf\u30b9\u30af resource.menu.propagation.list=\u4f1d\u64ad\u30bf\u30b9\u30af +resource.menu.livesync=Live Sync task +task.livesync=Live Sync task {0} diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_pt_BR.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_pt_BR.properties index 7bf53cd4e62..2c39dc5d858 100644 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_pt_BR.properties +++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_pt_BR.properties @@ -41,3 +41,5 @@ resource.menu.reconciliation=Reconciliation resource.menu.push.list=Push tasks resource.menu.pull.list=Pull tasks resource.menu.propagation.list=Propagation tasks +resource.menu.livesync=Live Sync task +task.livesync=Live Sync task {0} diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_ru.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_ru.properties index b017f0ecf36..4f3feddff75 100644 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_ru.properties +++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/topology/TopologyTogglePanel_ru.properties @@ -42,3 +42,5 @@ resource.menu.reconciliation=Reconciliation resource.menu.push.list=\u0417\u0430\u0434\u0430\u0447\u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 resource.menu.pull.list=\u0417\u0430\u0434\u0430\u0447\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 resource.menu.propagation.list=\u0417\u0430\u0434\u0430\u0447\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 +resource.menu.livesync=Live Sync task +task.livesync=Live Sync task {0} diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask.html b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask.html index aab535eef65..babf6006173 100644 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask.html +++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask.html @@ -26,7 +26,7 @@
    [conflictResolutionAction]
    -
    [nullValue]
    +
    [nullValue]
    diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask.properties index eed190d2f8d..d6483f43b2d 100644 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask.properties +++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask.properties @@ -19,4 +19,4 @@ matchingRule=Matching rule unmatchingRule=Unmatching rule provisioningActions=Pull Actions conflictResolutionAction=Conflict resolution action -pullCorrelationRule=Correlation rule +inboundCorrelationRule=Correlation rule diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_fr_CA.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_fr_CA.properties index fe255e28d59..a101fe02538 100644 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_fr_CA.properties +++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_fr_CA.properties @@ -19,4 +19,4 @@ matchingRule=R\u00e8gle de correspondance unmatchingRule=R\u00e8gle in\u00e9gal\u00e9e provisioningActions=Actions de pull conflictResolutionAction=Action de r\u00e9solution de conflits -pullCorrelationRule=R\u00e8gle de corr\u00e9lation +inboundCorrelationRule=R\u00e8gle de corr\u00e9lation diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_it.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_it.properties index 70de8695e0a..e81ad0cbe91 100644 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_it.properties +++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_it.properties @@ -19,4 +19,4 @@ matchingRule=Regola di matching unmatchingRule=Regola di unmatching provisioningActions=Azioni di pull conflictResolutionAction=Azione di Risoluzione Conflitti -pullCorrelationRule=Regola di Correlazione +inboundCorrelationRule=Regola di Correlazione diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_ja.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_ja.properties index eed190d2f8d..d6483f43b2d 100644 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_ja.properties +++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_ja.properties @@ -19,4 +19,4 @@ matchingRule=Matching rule unmatchingRule=Unmatching rule provisioningActions=Pull Actions conflictResolutionAction=Conflict resolution action -pullCorrelationRule=Correlation rule +inboundCorrelationRule=Correlation rule diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_pt_BR.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_pt_BR.properties index eed190d2f8d..d6483f43b2d 100644 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_pt_BR.properties +++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_pt_BR.properties @@ -19,4 +19,4 @@ matchingRule=Matching rule unmatchingRule=Unmatching rule provisioningActions=Pull Actions conflictResolutionAction=Conflict resolution action -pullCorrelationRule=Correlation rule +inboundCorrelationRule=Correlation rule diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_ru.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_ru.properties index eed190d2f8d..d6483f43b2d 100644 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_ru.properties +++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/CSVPullWizardBuilder$PullTask_ru.properties @@ -19,4 +19,4 @@ matchingRule=Matching rule unmatchingRule=Unmatching rule provisioningActions=Pull Actions conflictResolutionAction=Conflict resolution action -pullCorrelationRule=Correlation rule +inboundCorrelationRule=Correlation rule diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel.html b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel.html index 00b4882cb25..a48882b1754 100644 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel.html +++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel.html @@ -28,7 +28,7 @@ [panel for dynamic input type markup] - + [panel for dynamic input type markup] diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel.properties index c2b3da7d592..bac58ef6987 100644 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel.properties +++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel.properties @@ -16,6 +16,6 @@ # under the License. passwordPolicy=Password Policy accountPolicy=Account Policy -pullPolicy=Pull Policy +inboundPolicy=Inbound Policy pushPolicy=Push Policy propagationPolicy=Propagation Policy diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel_fr_CA.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel_fr_CA.properties index 2e20b1a9ec2..f8106ed4a56 100644 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel_fr_CA.properties +++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel_fr_CA.properties @@ -16,6 +16,6 @@ # under the License. passwordPolicy=Politique sur les mots de passe accountPolicy=Politique sur les comptes -pullPolicy=Politique Pull +inboundPolicy=Politique entrante pushPolicy=Politique Push propagationPolicy=Propagation Policy diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel_it.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel_it.properties index c2b3da7d592..bac58ef6987 100644 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel_it.properties +++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel_it.properties @@ -16,6 +16,6 @@ # under the License. passwordPolicy=Password Policy accountPolicy=Account Policy -pullPolicy=Pull Policy +inboundPolicy=Inbound Policy pushPolicy=Push Policy propagationPolicy=Propagation Policy diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel_ja.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel_ja.properties index 0df90b32e0e..02bfa23f796 100644 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel_ja.properties +++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel_ja.properties @@ -16,6 +16,6 @@ # under the License. passwordPolicy=\u30d1\u30b9\u30ef\u30fc\u30c9\u30dd\u30ea\u30b7\u30fc accountPolicy=\u30a2\u30ab\u30a6\u30f3\u30c8\u30dd\u30ea\u30b7\u30fc -pullPolicy=\u30d7\u30eb\u30dd\u30ea\u30b7\u30fc +inboundPolicy=Inbound Policy pushPolicy=\u30d7\u30c3\u30b7\u30e5\u30dd\u30ea\u30b7\u30fc propagationPolicy=Propagation Policy diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel_pt_BR.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel_pt_BR.properties index 2b8b4b76ee3..3ecdb7edf25 100644 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel_pt_BR.properties +++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel_pt_BR.properties @@ -16,6 +16,6 @@ # under the License. passwordPolicy=Pol\u00edtica de Senha accountPolicy=Pol\u00edtica de Conta -pullPolicy=Pol\u00edtica de Pull +inboundPolicy=Pol\u00edtica de Entrada pushPolicy=Push Policy propagationPolicy=Propagation Policy diff --git a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel_ru.properties b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel_ru.properties index 70bf684ab74..4e8655a09e5 100644 --- a/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel_ru.properties +++ b/client/idm/console/src/main/resources/org/apache/syncope/client/console/wizards/resources/ResourceSecurityPanel_ru.properties @@ -17,6 +17,6 @@ passwordPolicy = \u041f\u043e\u043b\u0438\u0442\u0438\u043a\u0430 \u043f\u0430\u0440\u043e\u043b\u0435\u0439 accountPolicy = \u041f\u043e\u043b\u0438\u0442\u0438\u043a\u0430 \u0443\u0447\u0435\u0442\u043d\u044b\u0445 \u0437\u0430\u043f\u0438\u0441\u0435\u0439 -pullPolicy=\u041f\u043e\u043b\u0438\u0442\u0438\u043a\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 +inboundPolicy=Inbound Policy pushPolicy=Push Policy propagationPolicy=Propagation Policy diff --git a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/AjaxWizard.java b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/AjaxWizard.java index 6297ab3c2e0..d47a4125363 100644 --- a/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/AjaxWizard.java +++ b/client/idrepo/common-ui/src/main/java/org/apache/syncope/client/ui/commons/wizards/AjaxWizard.java @@ -270,8 +270,8 @@ private Serializable onApply(final AjaxRequestTarget target) throws TimeoutExcep return res.getRight(); } catch (InterruptedException | ExecutionException e) { - if (e.getCause() instanceof CaptchaNotMatchingException) { - throw (CaptchaNotMatchingException) e.getCause(); + if (e.getCause() instanceof CaptchaNotMatchingException captchaNotMatchingException) { + throw captchaNotMatchingException; } throw new WicketRuntimeException(e); } diff --git a/client/idrepo/console/pom.xml b/client/idrepo/console/pom.xml index 75a55d57e9e..56e7f005ec8 100644 --- a/client/idrepo/console/pom.xml +++ b/client/idrepo/console/pom.xml @@ -339,11 +339,6 @@ under the License. - - org.springframework.boot - spring-boot-starter-undertow - - org.apache.syncope.common.keymaster.self syncope-common-keymaster-client-self diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/IdRepoImplementationInfoProvider.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/IdRepoImplementationInfoProvider.java index 4da290dfc49..8c0af971970 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/IdRepoImplementationInfoProvider.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/IdRepoImplementationInfoProvider.java @@ -211,6 +211,19 @@ protected List load() { }; } + @Override + public IModel> getLiveSyncDeltaMappers() { + return new LoadableDetachableModel<>() { + + private static final long serialVersionUID = 5275935387613157437L; + + @Override + protected List load() { + return List.of(); + } + }; + } + @Override public IModel> getMacroActions() { return new LoadableDetachableModel<>() { @@ -226,7 +239,7 @@ protected List load() { } @Override - public IModel> getPullActions() { + public IModel> getInboundActions() { return new LoadableDetachableModel<>() { private static final long serialVersionUID = 5275935387613157437L; diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/ImplementationInfoProvider.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/ImplementationInfoProvider.java index d01cde7d81c..b29c37bcdd3 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/ImplementationInfoProvider.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/commons/ImplementationInfoProvider.java @@ -46,9 +46,11 @@ enum ViewMode { IModel> getReconFilterBuilders(); + IModel> getLiveSyncDeltaMappers(); + IModel> getMacroActions(); - IModel> getPullActions(); + IModel> getInboundActions(); IModel> getPushActions(); } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DashboardSystemPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DashboardSystemPanel.java index ba3266bfefa..e7154909ae3 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DashboardSystemPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/DashboardSystemPanel.java @@ -55,9 +55,9 @@ public DashboardSystemPanel(final String id) { Pair gitAndBuildInfo = SyncopeConsoleSession.get().gitAndBuildInfo(); Label version = new Label("version", gitAndBuildInfo.getRight()); String versionLink = - StringUtils.isNotBlank(gitAndBuildInfo.getLeft()) && gitAndBuildInfo.getRight().endsWith("-SNAPSHOT") - ? "https://gitbox.apache.org/repos/asf?p=syncope.git;a=commit;h=" + gitAndBuildInfo.getLeft() - : "https://cwiki.apache.org/confluence/display/SYNCOPE/Maggiore"; + StringUtils.isNotBlank(gitAndBuildInfo.getLeft()) && gitAndBuildInfo.getRight().endsWith("-SNAPSHOT") + ? "https://gitbox.apache.org/repos/asf?p=syncope.git;a=commit;h=" + gitAndBuildInfo.getLeft() + : "https://cwiki.apache.org/confluence/display/SYNCOPE/Maggiore"; version.add(new AttributeModifier("onclick", "window.open('" + versionLink + "', '_blank')")); add(version); @@ -86,15 +86,14 @@ protected void onUpdate(final AjaxRequestTarget target) { @Override public void onClick() { try { - HttpResourceStream stream = - new HttpResourceStream(new ResponseHolder(syncopeRestClient.exportInternalStorageContent( - tableThresholdModel.getObject()))); + HttpResourceStream stream = new HttpResourceStream(new ResponseHolder( + syncopeRestClient.exportInternalStorageContent(tableThresholdModel.getObject()))); ResourceStreamRequestHandler rsrh = new ResourceStreamRequestHandler(stream); rsrh.setFileName( - stream.getFilename() == null - ? SyncopeConsoleSession.get().getDomain() + "Content.xml" - : stream.getFilename()); + stream.getFilename() == null + ? SyncopeConsoleSession.get().getDomain() + "Content.xml" + : stream.getFilename()); rsrh.setContentDisposition(ContentDisposition.ATTACHMENT); rsrh.setCacheDuration(Duration.ZERO); diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/TogglePanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/TogglePanel.java index c6f72fa274c..f994e43895b 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/TogglePanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/panels/TogglePanel.java @@ -162,26 +162,25 @@ protected String getTargetKey(final Serializable modelObject) { final String key; if (modelObject == null) { key = new ResourceModel("actions", StringUtils.EMPTY).getObject(); - } else if (modelObject instanceof EntityTO) { - key = ((EntityTO) modelObject).getKey(); + } else if (modelObject instanceof EntityTO entityTO) { + key = entityTO.getKey(); } else if (modelObject instanceof AnyWrapper) { key = ((AnyWrapper) modelObject).getInnerObject().getKey(); - } else if (modelObject instanceof Attr) { - key = ((Attr) modelObject).getSchema(); - } else if (modelObject instanceof ConfParam) { - key = ((ConfParam) modelObject).getSchema(); - } else if (modelObject instanceof StatusBean) { - key = StringUtils.isNotBlank(((StatusBean) modelObject).getResource()) - ? ((StatusBean) modelObject).getResource() : ((StatusBean) modelObject).getKey(); - } else if (modelObject instanceof PolicyRuleWrapper) { - key = ((PolicyRuleWrapper) modelObject).getConf().getName(); - } else if (modelObject instanceof JobTO) { - key = ((JobTO) modelObject).getRefKey() == null - ? ((JobTO) modelObject).getRefDesc() : ((JobTO) modelObject).getRefKey(); - } else if (modelObject instanceof ToggleableTarget) { - key = ((ToggleableTarget) modelObject).getKey(); - } else if (modelObject instanceof Domain) { - key = ((Domain) modelObject).getKey(); + } else if (modelObject instanceof Attr attr) { + key = attr.getSchema(); + } else if (modelObject instanceof ConfParam confParam) { + key = confParam.getSchema(); + } else if (modelObject instanceof StatusBean statusBean) { + key = StringUtils.isNotBlank(statusBean.getResource()) + ? statusBean.getResource() : statusBean.getKey(); + } else if (modelObject instanceof PolicyRuleWrapper policyRuleWrapper) { + key = policyRuleWrapper.getConf().getName(); + } else if (modelObject instanceof JobTO jobTO) { + key = jobTO.getRefKey() == null ? jobTO.getRefDesc() : jobTO.getRefKey(); + } else if (modelObject instanceof ToggleableTarget toggleableTarget) { + key = toggleableTarget.getKey(); + } else if (modelObject instanceof Domain domain) { + key = domain.getKey(); } else { key = new ResourceModel("actions", StringUtils.EMPTY).getObject(); } @@ -213,7 +212,7 @@ public void toggle(final AjaxRequestTarget target, final Serializable modelObjec * @param toggle toggle action. */ public void toggle(final AjaxRequestTarget target, final boolean toggle) { - final String selector = String.format("$(\"div#%s\")", activeId); + String selector = String.format("$(\"div#%s\")", activeId); if (toggle) { if (status == Status.INACTIVE) { target.add(TogglePanel.this.container); diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/policies/PolicyDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/policies/PolicyDirectoryPanel.java index 97c433b0777..e2ca9f4c090 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/policies/PolicyDirectoryPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/policies/PolicyDirectoryPanel.java @@ -136,7 +136,7 @@ protected List> getColumns() { columns.add(new CollectionPropertyColumn<>( new StringResourceModel("usedByResources", this), "usedByResources")); } - if (type != PolicyType.PULL && type != PolicyType.PUSH) { + if (type != PolicyType.INBOUND && type != PolicyType.PUSH) { columns.add(new CollectionPropertyColumn<>( new StringResourceModel("usedByRealms", this), "usedByRealms")); } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/policies/PolicyModalPanelBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/policies/PolicyModalPanelBuilder.java index d941896f32a..5e933702e8e 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/policies/PolicyModalPanelBuilder.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/policies/PolicyModalPanelBuilder.java @@ -245,7 +245,7 @@ protected void onUpdate(final AjaxRequestTarget target) { }); break; - case PULL: + case INBOUND: case PUSH: fields.add(new AjaxDropDownChoicePanel<>( "field", diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/LiveSyncTask.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/LiveSyncTask.java new file mode 100644 index 00000000000..12b74538004 --- /dev/null +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/LiveSyncTask.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.client.console.tasks; + +import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.client.console.panels.MultilevelPanel; +import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal; +import org.apache.syncope.common.lib.to.AnyTO; +import org.apache.syncope.common.lib.to.LiveSyncTaskTO; +import org.apache.wicket.PageReference; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.model.Model; +import org.apache.wicket.model.StringResourceModel; + +public class LiveSyncTask extends AbstractTasks { + + private static final long serialVersionUID = -4013796607157549641L; + + public LiveSyncTask( + final BaseModal baseModal, final String resource, final PageReference pageRef) { + + super(BaseModal.CONTENT_ID); + + MultilevelPanel mlp = new MultilevelPanel("tasks"); + add(mlp); + + mlp.setFirstLevel(new LiveSyncTaskDirectoryPanel(taskRestClient, baseModal, mlp, resource, pageRef) { + + private static final long serialVersionUID = -2195387360323687302L; + + @Override + protected void viewTaskExecs(final LiveSyncTaskTO taskTO, final AjaxRequestTarget target) { + mlp.next( + new StringResourceModel("task.view", this, new Model<>(Pair.of(null, taskTO))).getObject(), + new TaskExecutionDetails<>(taskTO, pageRef), + target); + } + }); + } +} diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/LiveSyncTaskDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/LiveSyncTaskDirectoryPanel.java new file mode 100644 index 00000000000..2d5a30e4f59 --- /dev/null +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/LiveSyncTaskDirectoryPanel.java @@ -0,0 +1,112 @@ +/* + * 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.client.console.tasks; + +import java.util.Collection; +import java.util.List; +import java.util.Set; +import org.apache.syncope.client.console.commons.IdRepoConstants; +import org.apache.syncope.client.console.panels.MultilevelPanel; +import org.apache.syncope.client.console.rest.TaskRestClient; +import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal; +import org.apache.syncope.client.console.wicket.markup.html.form.ActionLink; +import org.apache.syncope.client.console.wicket.markup.html.form.ActionsPanel; +import org.apache.syncope.common.lib.to.LiveSyncTaskTO; +import org.apache.syncope.common.lib.types.IdRepoEntitlement; +import org.apache.syncope.common.lib.types.TaskType; +import org.apache.wicket.PageReference; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; +import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn; +import org.apache.wicket.model.IModel; + +public abstract class LiveSyncTaskDirectoryPanel extends ProvisioningTaskDirectoryPanel { + + private static final long serialVersionUID = 4984337552918213290L; + + protected LiveSyncTaskDirectoryPanel( + final TaskRestClient restClient, + final BaseModal baseModal, + final MultilevelPanel multiLevelPanelRef, + final String resource, + final PageReference pageRef) { + + super(restClient, baseModal, multiLevelPanelRef, TaskType.LIVE_SYNC, new LiveSyncTaskTO(), resource, pageRef); + } + + @Override + protected List> getTrailingFieldColumns() { + List> columns = super.getTrailingFieldColumns(); + columns.removeIf(column -> column instanceof PropertyColumn propertyColumn + && propertyColumn.getPropertyExpression().contains("Exec")); + return columns; + } + + @Override + protected Collection getBatches() { + return Set.of(); + } + + @Override + public ActionsPanel getActions(final IModel model) { + ActionsPanel panel = super.getActions(model); + panel.getActions().removeIf(action -> action.getType() == ActionLink.ActionType.CLONE); + return panel; + } + + @Override + protected String paginatorRowsKey() { + return IdRepoConstants.PREF_PULL_TASKS_PAGINATOR_ROWS; + } + + @Override + protected void onDelete(final AjaxRequestTarget target) { + target.add(addAjaxLink); + } + + @Override + protected ProvisioningTasksProvider dataProvider() { + return new ProvisioningTasksProvider<>(TaskType.LIVE_SYNC, rows) { + + private static final long serialVersionUID = -8658004154067744714L; + + @Override + public long size() { + long size = super.size(); + addAjaxLink.setEnabled(size == 0); + return size; + } + }; + } + + @Override + protected void addFurtherActions(final ActionsPanel panel, final IModel model) { + panel.add(new ActionLink<>() { + + private static final long serialVersionUID = -3722207913631435501L; + + @Override + public void onClick(final AjaxRequestTarget target, final LiveSyncTaskTO ignore) { + LiveSyncTaskDirectoryPanel.this.getTogglePanel().close(target); + templates.setTargetObject(model.getObject()); + templates.toggle(target, true); + } + }, ActionLink.ActionType.TEMPLATE, IdRepoEntitlement.TASK_UPDATE).disableIndicator(); + } +} diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/ProvisioningTaskDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/ProvisioningTaskDirectoryPanel.java index 6d14acf94f9..2824293f615 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/ProvisioningTaskDirectoryPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/ProvisioningTaskDirectoryPanel.java @@ -27,8 +27,8 @@ import org.apache.syncope.client.console.wicket.ajax.IndicatorAjaxTimerBehavior; import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal; import org.apache.syncope.client.console.widgets.JobActionPanel; +import org.apache.syncope.common.lib.to.InboundTaskTO; import org.apache.syncope.common.lib.to.ProvisioningTaskTO; -import org.apache.syncope.common.lib.to.PullTaskTO; import org.apache.syncope.common.lib.to.PushTaskTO; import org.apache.syncope.common.lib.types.TaskType; import org.apache.wicket.PageReference; @@ -99,7 +99,7 @@ protected List> getFieldColumns() { columns.addAll(getHeadingFieldColumns()); - if (schedTaskTO instanceof PullTaskTO) { + if (schedTaskTO instanceof InboundTaskTO) { columns.add(new PropertyColumn<>( new StringResourceModel("destinationRealm", this), "destinationRealm", "destinationRealm")); } else if (schedTaskTO instanceof PushTaskTO) { @@ -114,9 +114,9 @@ protected List> getFieldColumns() { @Override public void onEvent(final IEvent event) { - if (event.getPayload() instanceof JobActionPanel.JobActionPayload) { + if (event.getPayload() instanceof JobActionPanel.JobActionPayload payload) { container.modelChanged(); - JobActionPanel.JobActionPayload.class.cast(event.getPayload()).getTarget().add(container); + payload.getTarget().add(container); } else { super.onEvent(event); } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/PullTaskDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/PullTaskDirectoryPanel.java index 1857c939bfd..b1754d0c057 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/PullTaskDirectoryPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/PullTaskDirectoryPanel.java @@ -47,7 +47,7 @@ protected PullTaskDirectoryPanel( @Override protected String paginatorRowsKey() { - return IdRepoConstants.PREF_PUSH_TASKS_PAGINATOR_ROWS; + return IdRepoConstants.PREF_PULL_TASKS_PAGINATOR_ROWS; } @Override diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel.java index 1f052d495ad..079c3909904 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel.java @@ -255,6 +255,10 @@ protected final List> getColumns() { return columns; } + protected void onDelete(final AjaxRequestTarget target) { + // nothing to do + } + @Override public ActionsPanel getActions(final IModel model) { ActionsPanel panel = super.getActions(model); @@ -277,10 +281,10 @@ public void onClick(final AjaxRequestTarget target, final T ignore) { @Override public void onClick(final AjaxRequestTarget target, final T ignore) { + SchedTaskDirectoryPanel.this.getTogglePanel().close(target); send(SchedTaskDirectoryPanel.this, Broadcast.EXACT, new AjaxWizard.EditItemActionEvent<>( - restClient.readTask(taskType, model.getObject().getKey()), - target).setTitleModel( + restClient.readTask(taskType, model.getObject().getKey()), target).setTitleModel( new StringResourceModel("inner.task.edit", SchedTaskDirectoryPanel.this, Model.of(Pair.of(ActionLink.ActionType.EDIT, model.getObject()))))); @@ -294,7 +298,7 @@ public void onClick(final AjaxRequestTarget target, final T ignore) { @Override public void onClick(final AjaxRequestTarget target, final T ignore) { SchedTaskDirectoryPanel.this.getTogglePanel().close(target); - final T clone = SerializationUtils.clone(model.getObject()); + T clone = SerializationUtils.clone(model.getObject()); clone.setKey(null); send(SchedTaskDirectoryPanel.this, Broadcast.EXACT, new AjaxWizard.EditItemActionEvent<>(clone, target).setTitleModel( @@ -327,6 +331,8 @@ public void onClick(final AjaxRequestTarget target, final T ignore) { try { restClient.delete(taskType, taskTO.getKey()); + onDelete(target); + SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED)); target.add(container); SchedTaskDirectoryPanel.this.getTogglePanel().close(target); diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java index 05be652a5e8..dbf73c9ecaa 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder.java @@ -39,6 +39,8 @@ import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel; import org.apache.syncope.client.ui.commons.markup.html.form.FieldPanel; import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.to.InboundTaskTO; +import org.apache.syncope.common.lib.to.LiveSyncTaskTO; import org.apache.syncope.common.lib.to.MacroTaskTO; import org.apache.syncope.common.lib.to.ProvisioningTaskTO; import org.apache.syncope.common.lib.to.PullTaskTO; @@ -97,7 +99,7 @@ protected Serializable onApplyInternal(final SchedTaskTO modelObject) { wrapper.fillFilterConditions(); } - modelObject.setCronExpression(crontabPanel.getCronExpression()); + Optional.ofNullable(crontabPanel).ifPresent(cp -> modelObject.setCronExpression(cp.getCronExpression())); if (modelObject.getKey() == null) { taskRestClient.create(type, modelObject); } else { @@ -109,11 +111,13 @@ protected Serializable onApplyInternal(final SchedTaskTO modelObject) { @Override protected WizardModel buildModelSteps(final SchedTaskTO modelObject, final WizardModel wizardModel) { wizardModel.add(new Profile(modelObject)); - if (modelObject instanceof PushTaskTO) { - wrapper = new PushTaskWrapper(PushTaskTO.class.cast(modelObject)); + if (modelObject instanceof PushTaskTO pushTask) { + wrapper = new PushTaskWrapper(pushTask); wizardModel.add(new PushTaskFilters(wrapper, pageRef)); } - wizardModel.add(new Schedule(modelObject)); + if (!(modelObject instanceof LiveSyncTaskTO)) { + wizardModel.add(new Schedule(modelObject)); + } return wizardModel; } @@ -134,11 +138,14 @@ protected class Profile extends WizardStep { protected final IModel> reconFilterBuilders = SyncopeWebApplication.get(). getImplementationInfoProvider().getReconFilterBuilders(); + protected final IModel> liveSyncDeltaMappers = SyncopeWebApplication.get(). + getImplementationInfoProvider().getLiveSyncDeltaMappers(); + protected final IModel> macroActions = SyncopeWebApplication.get(). getImplementationInfoProvider().getMacroActions(); - protected final IModel> pullActions = SyncopeWebApplication.get(). - getImplementationInfoProvider().getPullActions(); + protected final IModel> inboundActions = SyncopeWebApplication.get(). + getImplementationInfoProvider().getInboundActions(); protected final IModel> pushActions = SyncopeWebApplication.get(). getImplementationInfoProvider().getPushActions(); @@ -191,9 +198,9 @@ protected Iterator getChoices(final String input) { : List.of()).iterator(); } }; - if (taskTO instanceof MacroTaskTO) { + if (taskTO instanceof MacroTaskTO macroTask) { realm.addRequiredLabel(); - if (StringUtils.isBlank(MacroTaskTO.class.cast(taskTO).getRealm())) { + if (StringUtils.isBlank(macroTask.getRealm())) { // add a default destination realm if missing in the task realm.setModelObject(SyncopeConstants.ROOT_REALM); } @@ -213,14 +220,39 @@ protected Iterator getChoices(final String input) { macroTaskSpecifics.add(saveExecs); // ------------------------------ - // Only for pull tasks + // Only for live sync tasks // ------------------------------ + WebMarkupContainer liveSyncTaskSpecifics = new WebMarkupContainer("liveSyncTaskSpecifics"); + add(liveSyncTaskSpecifics.setRenderBodyOnly(true)); + + boolean isMapped = false; + if (taskTO instanceof LiveSyncTaskTO) { + isMapped = true; + } else { + liveSyncTaskSpecifics.setEnabled(false).setVisible(false); + } + + liveSyncTaskSpecifics.add(destinationRealm("liveSyncDestinationRealm", taskTO, settings)); + + liveSyncTaskSpecifics.add(remediation("liveSyncRemediation", taskTO)); + + AjaxDropDownChoicePanel liveSyncDeltaMapper = new AjaxDropDownChoicePanel<>( + "liveSyncDeltaMapper", "liveSyncDeltaMapper", + new PropertyModel<>(taskTO, "liveSyncDeltaMapper"), false); + liveSyncDeltaMapper.setChoices(liveSyncDeltaMappers.getObject()); + liveSyncDeltaMapper.setEnabled(isMapped); + liveSyncDeltaMapper.setRequired(isMapped); + liveSyncTaskSpecifics.add(liveSyncDeltaMapper); + + // ------------------------------ + // Only for pull tasks + // ------------------------------ WebMarkupContainer pullTaskSpecifics = new WebMarkupContainer("pullTaskSpecifics"); add(pullTaskSpecifics.setRenderBodyOnly(true)); boolean isFiltered = false; - if (taskTO instanceof PullTaskTO) { - isFiltered = PullTaskTO.class.cast(taskTO).getPullMode() == PullMode.FILTERED_RECONCILIATION; + if (taskTO instanceof PullTaskTO pullTask) { + isFiltered = pullTask.getPullMode() == PullMode.FILTERED_RECONCILIATION; } else { pullTaskSpecifics.setEnabled(false).setVisible(false); } @@ -254,32 +286,9 @@ protected void onUpdate(final AjaxRequestTarget target) { } }); - AjaxSearchFieldPanel destinationRealm = - new AjaxSearchFieldPanel("destinationRealm", "destinationRealm", - new PropertyModel<>(taskTO, "destinationRealm"), settings) { + pullTaskSpecifics.add(destinationRealm("pullDestinationRealm", taskTO, settings)); - private static final long serialVersionUID = -6390474600233486704L; - - @Override - protected Iterator getChoices(final String input) { - return (RealmsUtils.checkInput(input) - ? searchRealms(input) - : List.of()).iterator(); - } - }; - - if (taskTO instanceof PullTaskTO) { - destinationRealm.addRequiredLabel(); - if (StringUtils.isBlank(PullTaskTO.class.cast(taskTO).getDestinationRealm())) { - // add a default destination realm if missing in the task - destinationRealm.setModelObject(SyncopeConstants.ROOT_REALM); - } - } - pullTaskSpecifics.add(destinationRealm); - - AjaxCheckBoxPanel remediation = new AjaxCheckBoxPanel( - "remediation", "remediation", new PropertyModel<>(taskTO, "remediation"), false); - pullTaskSpecifics.add(remediation); + pullTaskSpecifics.add(remediation("pullRemediation", taskTO)); // ------------------------------ // Only for push tasks @@ -331,7 +340,7 @@ protected Iterator getChoices(final String input) { build("actions", new PropertyModel<>(taskTO, "actions"), new ListModel<>(taskTO instanceof PushTaskTO - ? pushActions.getObject() : pullActions.getObject())); + ? pushActions.getObject() : inboundActions.getObject())); provisioningTaskSpecifics.add(actions.setOutputMarkupId(true)); AjaxDropDownChoicePanel matchingRule = new AjaxDropDownChoicePanel<>( @@ -412,6 +421,42 @@ protected void onUpdate(final AjaxRequestTarget target) { } }); } + + private AjaxSearchFieldPanel destinationRealm( + final String id, + final SchedTaskTO taskTO, + final AutoCompleteSettings settings) { + + AjaxSearchFieldPanel destinationRealm = new AjaxSearchFieldPanel( + id, + "destinationRealm", + new PropertyModel<>(taskTO, "destinationRealm"), + settings) { + + private static final long serialVersionUID = -6390474600233486704L; + + @Override + protected Iterator getChoices(final String input) { + return (RealmsUtils.checkInput(input) + ? searchRealms(input) + : List.of()).iterator(); + } + }; + + if (taskTO instanceof InboundTaskTO inboundTask) { + destinationRealm.addRequiredLabel(); + if (StringUtils.isBlank(inboundTask.getDestinationRealm())) { + // add a default destination realm if missing in the task + destinationRealm.setModelObject(SyncopeConstants.ROOT_REALM); + } + } + + return destinationRealm; + } + + private AjaxCheckBoxPanel remediation(final String id, final SchedTaskTO taskTO) { + return new AjaxCheckBoxPanel(id, "remediation", new PropertyModel<>(taskTO, "remediation"), false); + } } protected static class ConcurrentSettingsValueModel implements IModel { diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/bootstrap/dialog/BaseModal.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/bootstrap/dialog/BaseModal.java index ab3d6588940..e2248a7f536 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/bootstrap/dialog/BaseModal.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/bootstrap/dialog/BaseModal.java @@ -136,8 +136,8 @@ public T getFormModel() { } public ModalPanel getContent() { - if (content instanceof ModalPanel) { - return (ModalPanel) content; + if (content instanceof ModalPanel modalPanel) { + return modalPanel; } throw new IllegalStateException(); } diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java index 44d65ab3099..a04d0a9b275 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/wicket/markup/html/form/ActionLink.java @@ -100,6 +100,7 @@ public enum ActionType { PROPAGATION_TASKS("read"), NOTIFICATION_TASKS("read"), PULL_TASKS("read"), + LIVE_SYNC_TASK("read"), PUSH_TASKS("read"), ZOOM_IN("zoomin"), ZOOM_OUT("zoomout"), diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java index 3a9f51b73ca..59f219287b0 100644 --- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java +++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/widgets/JobWidget.java @@ -395,10 +395,10 @@ protected ActionsPanel getActions(final IModel model) { @Override public void onClick(final AjaxRequestTarget target, final JobTO ignore) { switch (jobTO.getType()) { - case NOTIFICATION: - break; + case NOTIFICATION -> { + } - case REPORT: + case REPORT -> { ReportTO reportTO = reportRestClient.read(jobTO.getRefKey()); ReportWizardBuilder rwb = new ReportWizardBuilder( @@ -417,9 +417,9 @@ public void onClick(final AjaxRequestTarget target, final JobTO ignore) { new Model<>(reportTO))); jobModal.show(true); - break; + } - case TASK: + case TASK -> { TaskType taskType = null; if (jobTO.getRefDesc().startsWith("SCHEDULED")) { taskType = TaskType.SCHEDULED; @@ -444,10 +444,9 @@ public void onClick(final AjaxRequestTarget target, final JobTO ignore) { break; } - if (taskTO instanceof ProvisioningTaskTO) { - SchedTaskWizardBuilder swb = - new SchedTaskWizardBuilder<>(taskType, (ProvisioningTaskTO) taskTO, - realmRestClient, taskRestClient, pageRef); + if (taskTO instanceof ProvisioningTaskTO provisioningTask) { + SchedTaskWizardBuilder swb = new SchedTaskWizardBuilder<>( + taskType, provisioningTask, realmRestClient, taskRestClient, pageRef); swb.setEventSink(AvailableJobsPanel.this); target.add(jobModal.setContent(swb.build(BaseModal.CONTENT_ID, AjaxWizard.Mode.EDIT))); @@ -462,10 +461,10 @@ public void onClick(final AjaxRequestTarget target, final JobTO ignore) { SyncopeConsoleSession.get().info("Unsupported task type: " + taskType.name()); ((BasePage) pageRef.getPage()).getNotificationPanel().refresh(target); } - break; + } - default: - break; + default -> { + } } } @@ -485,19 +484,17 @@ public void onClick(final AjaxRequestTarget target, final JobTO ignore) { if (null != jobTO.getType()) { switch (jobTO.getType()) { - case NOTIFICATION: - break; + case NOTIFICATION -> { + } - case REPORT: + case REPORT -> reportRestClient.actionJob(jobTO.getRefKey(), JobAction.DELETE); - break; - case TASK: + case TASK -> taskRestClient.actionJob(jobTO.getRefKey(), JobAction.DELETE); - break; - default: - break; + default -> { + } } SyncopeConsoleSession.get().success(getString(Constants.OPERATION_SUCCEEDED)); target.add(container); diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPullActions.groovy b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyInboundActions.groovy similarity index 86% rename from client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPullActions.groovy rename to client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyInboundActions.groovy index 3d7de667699..1e0156f2fd8 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPullActions.groovy +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyInboundActions.groovy @@ -31,11 +31,11 @@ import org.apache.syncope.core.provisioning.api.job.JobExecutionException import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningActions import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile -import org.apache.syncope.core.provisioning.api.pushpull.PullActions -import org.identityconnectors.framework.common.objects.SyncDelta +import org.apache.syncope.core.provisioning.api.pushpull.InboundActions +import org.identityconnectors.framework.common.objects.LiveSyncDelta @CompileStatic -class MyPullActions implements PullActions { +class MyInboundActions implements InboundActions { @Override Set moreAttrsToGet(ProvisioningProfile profile, OrgUnit orgUnit) { @@ -48,14 +48,14 @@ class MyPullActions implements PullActions { } @Override - SyncDelta preprocess(ProvisioningProfile profile, SyncDelta delta) { + LiveSyncDelta preprocess(ProvisioningProfile profile, LiveSyncDelta delta) { return delta; } @Override void beforeProvision( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, AnyCR anyCR) throws JobExecutionException { } @@ -63,7 +63,7 @@ class MyPullActions implements PullActions { @Override void beforeProvision( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, RealmTO realmTO) throws JobExecutionException { } @@ -71,7 +71,7 @@ class MyPullActions implements PullActions { @Override void beforeAssign( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, AnyCR anyCR) throws JobExecutionException { } @@ -79,7 +79,7 @@ class MyPullActions implements PullActions { @Override void beforeAssign( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, RealmTO realmTO) throws JobExecutionException { } @@ -87,7 +87,7 @@ class MyPullActions implements PullActions { @Override void beforeUnassign( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, EntityTO entity) throws JobExecutionException { } @@ -95,7 +95,7 @@ class MyPullActions implements PullActions { @Override void beforeDeprovision( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, EntityTO entity) throws JobExecutionException { } @@ -103,7 +103,7 @@ class MyPullActions implements PullActions { @Override void beforeUnlink( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, EntityTO entity) throws JobExecutionException { } @@ -111,7 +111,7 @@ class MyPullActions implements PullActions { @Override void beforeLink( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, EntityTO entity) throws JobExecutionException { } @@ -119,7 +119,7 @@ class MyPullActions implements PullActions { @Override void beforeUpdate( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, EntityTO entity, AnyUR anyUR) throws JobExecutionException { @@ -128,7 +128,7 @@ class MyPullActions implements PullActions { @Override void beforeDelete( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, EntityTO entity) throws JobExecutionException { } @@ -136,7 +136,7 @@ class MyPullActions implements PullActions { @Override void after( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, EntityTO entity, ProvisioningReport result) throws JobExecutionException { @@ -146,7 +146,7 @@ class MyPullActions implements PullActions { @Override IgnoreProvisionException onError( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, Exception e) throws JobExecutionException { return null; diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyInboundCorrelationRule.groovy b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyInboundCorrelationRule.groovy new file mode 100644 index 00000000000..e3d24d45882 --- /dev/null +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyInboundCorrelationRule.groovy @@ -0,0 +1,32 @@ +/* + * 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. + */ +import groovy.transform.CompileStatic +import org.apache.syncope.common.lib.to.Provision +import org.apache.syncope.core.persistence.api.dao.InboundCorrelationRule +import org.apache.syncope.core.persistence.api.dao.search.SearchCond +import org.identityconnectors.framework.common.objects.LiveSyncDelta + +@CompileStatic +class MyInboundCorrelationRule implements InboundCorrelationRule { + + @Override + SearchCond getSearchCond(LiveSyncDelta syncDelta, Provision provision) { + + } +} diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPullCorrelationRule.groovy b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyLiveSyncDeltaMapper.groovy similarity index 78% rename from client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPullCorrelationRule.groovy rename to client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyLiveSyncDeltaMapper.groovy index c4e3a8faecc..92bf1d3455e 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPullCorrelationRule.groovy +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MyLiveSyncDeltaMapper.groovy @@ -18,15 +18,15 @@ */ import groovy.transform.CompileStatic import org.apache.syncope.common.lib.to.Provision -import org.apache.syncope.core.persistence.api.dao.PullCorrelationRule -import org.apache.syncope.core.persistence.api.dao.search.SearchCond +import org.apache.syncope.core.provisioning.api.LiveSyncDeltaMapper +import org.identityconnectors.framework.common.objects.LiveSyncDelta import org.identityconnectors.framework.common.objects.SyncDelta @CompileStatic -class MyPullCorrelationRule implements PullCorrelationRule { - +class MyLiveSyncDeltaMapper implements LiveSyncDeltaMapper { + @Override - SearchCond getSearchCond(SyncDelta syncDelta, Provision provision) { + SyncDelta map(LiveSyncDelta liveSyncDelta, Provision provision) { } } diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MySchedTaskJobDelegate.groovy b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MySchedTaskJobDelegate.groovy index 7012accddb4..50ecae3be6b 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MySchedTaskJobDelegate.groovy +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/implementations/MySchedTaskJobDelegate.groovy @@ -26,7 +26,7 @@ import org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate class MySchedTaskJobDelegate implements SchedTaskJobDelegate { @Override - void execute(TaskType taskType, String taskKey, boolean dryRun, JobExecutionContext context) + void execute(TaskType taskType, String taskKey, JobExecutionContext context) throws JobExecutionException { } diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies.properties index 0f8d43457cb..1399fbc0b58 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies.properties @@ -16,7 +16,7 @@ # under the License. policy.account=Account policy.password=Password -policy.pull=Pull +policy.inbound=Inbound policy.push=Push policy.access=Access policy.attrRelease=Attribute Release diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_it.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_it.properties index db16fe95fea..70eef1e6858 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_it.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_it.properties @@ -16,7 +16,7 @@ # under the License. policy.account=Account policy.password=Password -policy.pull=Pull +policy.inbound=Inbound policy.push=Push policy.access=Accesso policy.attrRelease=Rilascio Attributi diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_ja.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_ja.properties index 76212b4d7fa..00c687af80e 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_ja.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_ja.properties @@ -16,7 +16,7 @@ # under the License. policy.account=\u30a2\u30ab\u30a6\u30f3\u30c8 policy.password=\u30d1\u30b9\u30ef\u30fc\u30c9 -policy.pull=\u30d7\u30eb +policy.inbound=Inbound policy.push=\u30d7\u30c3\u30b7\u30e5 policy.access=Access policy.attrRelease=Attribute Release diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_pt_BR.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_pt_BR.properties index 0f8d43457cb..1399fbc0b58 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_pt_BR.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_pt_BR.properties @@ -16,7 +16,7 @@ # under the License. policy.account=Account policy.password=Password -policy.pull=Pull +policy.inbound=Inbound policy.push=Push policy.access=Access policy.attrRelease=Attribute Release diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_ru.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_ru.properties index 0d5f9b22231..91c46b55d79 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_ru.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_ru.properties @@ -17,7 +17,7 @@ # policy.account=\u041f\u043e\u043b\u0438\u0442\u0438\u043a\u0430 \u0443\u0447\u0435\u0442\u043d\u044b\u0445 \u0437\u0430\u043f\u0438\u0441\u0435\u0439 policy.password=\u041f\u043e\u043b\u0438\u0442\u0438\u043a\u0430 \u043f\u0430\u0440\u043e\u043b\u0435\u0439 -policy.pull=\u041f\u043e\u043b\u0438\u0442\u0438\u043a\u0430 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 +policy.inbound=Inbound policy.push=Push policy.access=Access policy.attrRelease=Attribute Release diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel.properties index 70dbbd8e89d..5c944691d60 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel.properties @@ -31,3 +31,4 @@ reconFilterBuilder=Reconciliation Filter Builder actions=Actions sourceRealm=Source Realm realm=Realm +liveSyncDeltaMapper=LiveSyncDelta Mapper diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel_fr_CA.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel_fr_CA.properties index cc45c5c4fa5..d1fe946ef1a 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel_fr_CA.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel_fr_CA.properties @@ -30,3 +30,4 @@ reconFilterBuilder=G\u00e9n\u00e9rateur de filtre de rapprochement actions=Actions sourceRealm=Domaine source realm=Realm +liveSyncDeltaMapper=LiveSyncDelta Mapper diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel_it.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel_it.properties index 0444fd6dc5f..0ef2020ada9 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel_it.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel_it.properties @@ -31,3 +31,4 @@ reconFilterBuilder=Reconciliation Filter Builder actions=Actions sourceRealm=Realm sorgente realm=Realm +liveSyncDeltaMapper=LiveSyncDelta Mapper diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel_ja.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel_ja.properties index 8d8cf9bfb93..da2cc2f9b36 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel_ja.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel_ja.properties @@ -31,3 +31,4 @@ reconFilterBuilder=\u30d5\u30a3\u30eb\u30bf\u30fc\u30d3\u30eb\u30c0\u30fc\u306e\ actions=\u30a2\u30af\u30b7\u30e7\u30f3 sourceRealm=\u30bd\u30fc\u30b9\u30ec\u30eb\u30e0 realm=Realm +liveSyncDeltaMapper=LiveSyncDelta Mapper diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel_pt_BR.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel_pt_BR.properties index 70dbbd8e89d..5c944691d60 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel_pt_BR.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel_pt_BR.properties @@ -31,3 +31,4 @@ reconFilterBuilder=Reconciliation Filter Builder actions=Actions sourceRealm=Source Realm realm=Realm +liveSyncDeltaMapper=LiveSyncDelta Mapper diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel_ru.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel_ru.properties index 99490c04cdd..0f821083538 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel_ru.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskDirectoryPanel_ru.properties @@ -42,3 +42,4 @@ reconFilterBuilder=\u0424\u0438\u043b\u044c\u0442\u0440 \u0440\u0435\u043a\u043e actions=\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044f sourceRealm=Source Realm realm=Realm +liveSyncDeltaMapper=LiveSyncDelta Mapper diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder$Profile.html b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder$Profile.html index 7c4f21b1537..fea4aab8d0e 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder$Profile.html +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/tasks/SchedTaskWizardBuilder$Profile.html @@ -31,11 +31,17 @@
    [saveExecs]
    + +
    [destinationRealm]
    +
    [filter]
    +
    [remediation]
    +
    + -
    [destinationRealm]
    +
    [destinationRealm]
    [pullMode]
    [filter]
    -
    [remediation]
    +
    [remediation]
    diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel.properties index b8d5d685eec..d7ba2e73999 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel.properties @@ -245,6 +245,10 @@ pull_tasks.class=fa fa-chevron-circle-left pull_tasks.title=pull tasks pull_tasks.alt=pull tasks icon +live_sync_task.class=fa-solid fa-rotate +live_sync_task.title=live sync task +live_sync_task.alt=live sync task icon + push_tasks.class=fa fa-chevron-circle-right push_tasks.title=push tasks push_tasks.alt=push tasks icon diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_fr_CA.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_fr_CA.properties index b42f43a8552..3169b9293a0 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_fr_CA.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_fr_CA.properties @@ -227,3 +227,6 @@ up.title=move up down.alt=down icon down.class=fas fa-arrow-down down.title=move down +live_sync_task.class=fa-solid fa-rotate +live_sync_task.title=live sync task +live_sync_task.alt=live sync task icon diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_it.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_it.properties index e5d20e04461..ea1f43d7ad3 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_it.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_it.properties @@ -282,3 +282,6 @@ up.title=sposta su down.alt=down icon down.class=fas fa-arrow-down down.title=sposta gi\u00f9 +live_sync_task.class=fa-solid fa-rotate +live_sync_task.title=task di live sync +live_sync_task.alt=live sync task icon diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ja.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ja.properties index 1a97e92fb51..2fc0a6661fd 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ja.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ja.properties @@ -283,3 +283,6 @@ up.title=move up down.alt=down icon down.class=fas fa-arrow-down down.title=move down +live_sync_task.class=fa-solid fa-rotate +live_sync_task.title=live sync task +live_sync_task.alt=live sync task icon diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_pt_BR.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_pt_BR.properties index dbb9c181a49..b70b246f77e 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_pt_BR.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_pt_BR.properties @@ -287,3 +287,6 @@ up.title=move up down.alt=down icon down.class=fas fa-arrow-down down.title=move down +live_sync_task.class=fa-solid fa-rotate +live_sync_task.title=live sync task +live_sync_task.alt=live sync task icon diff --git a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ru.properties b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ru.properties index 7277a38550c..eff41a0fbdb 100644 --- a/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ru.properties +++ b/client/idrepo/console/src/main/resources/org/apache/syncope/client/console/wicket/markup/html/form/ActionsPanel_ru.properties @@ -283,3 +283,6 @@ up.title=move up down.alt=down icon down.class=fas fa-arrow-down down.title=move down +live_sync_task.class=fa-solid fa-rotate +live_sync_task.title=live sync task +live_sync_task.alt=live sync task icon diff --git a/client/idrepo/enduser/pom.xml b/client/idrepo/enduser/pom.xml index 73f5bc57d08..0585d5fb5ea 100644 --- a/client/idrepo/enduser/pom.xml +++ b/client/idrepo/enduser/pom.xml @@ -288,11 +288,6 @@ under the License. - - org.springframework.boot - spring-boot-starter-undertow - - org.apache.syncope.common.keymaster.self syncope-common-keymaster-client-self diff --git a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultPullCorrelationRuleConf.java b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultInboundCorrelationRuleConf.java similarity index 92% rename from common/idm/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultPullCorrelationRuleConf.java rename to common/idm/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultInboundCorrelationRuleConf.java index e19003eb1e1..5e5ac4bb974 100644 --- a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultPullCorrelationRuleConf.java +++ b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultInboundCorrelationRuleConf.java @@ -23,7 +23,8 @@ import java.util.ArrayList; import java.util.List; -public class DefaultPullCorrelationRuleConf extends AbstractCorrelationRuleConf implements PullCorrelationRuleConf { +public class DefaultInboundCorrelationRuleConf + extends AbstractCorrelationRuleConf implements InboundCorrelationRuleConf { private static final long serialVersionUID = 429126085793346273L; diff --git a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/policy/PullCorrelationRuleConf.java b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/policy/InboundCorrelationRuleConf.java similarity index 93% rename from common/idm/lib/src/main/java/org/apache/syncope/common/lib/policy/PullCorrelationRuleConf.java rename to common/idm/lib/src/main/java/org/apache/syncope/common/lib/policy/InboundCorrelationRuleConf.java index 9c6e8505dec..7df5bd2abd7 100644 --- a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/policy/PullCorrelationRuleConf.java +++ b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/policy/InboundCorrelationRuleConf.java @@ -19,6 +19,6 @@ package org.apache.syncope.common.lib.policy; @FunctionalInterface -public interface PullCorrelationRuleConf extends RuleConf { +public interface InboundCorrelationRuleConf extends RuleConf { } diff --git a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/policy/PullPolicyTO.java b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/policy/InboundPolicyTO.java similarity index 91% rename from common/idm/lib/src/main/java/org/apache/syncope/common/lib/policy/PullPolicyTO.java rename to common/idm/lib/src/main/java/org/apache/syncope/common/lib/policy/InboundPolicyTO.java index 6fec12716c4..8e454f44cb1 100644 --- a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/policy/PullPolicyTO.java +++ b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/policy/InboundPolicyTO.java @@ -23,14 +23,14 @@ import io.swagger.v3.oas.annotations.media.Schema; @Schema(allOf = { ProvisioningPolicyTO.class }) -public class PullPolicyTO extends ProvisioningPolicyTO { +public class InboundPolicyTO extends ProvisioningPolicyTO { private static final long serialVersionUID = 993024634238024242L; @JacksonXmlProperty(localName = "_class", isAttribute = true) @JsonProperty("_class") @Schema(name = "_class", requiredMode = Schema.RequiredMode.REQUIRED, - example = "org.apache.syncope.common.lib.policy.PullPolicyTO") + example = "org.apache.syncope.common.lib.policy.InboundPolicyTO") @Override public String getDiscriminator() { return getClass().getName(); diff --git a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/policy/ProvisioningPolicyTO.java b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/policy/ProvisioningPolicyTO.java index daf2702656e..b3a5a15f352 100644 --- a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/policy/ProvisioningPolicyTO.java +++ b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/policy/ProvisioningPolicyTO.java @@ -27,7 +27,7 @@ import org.apache.syncope.common.lib.types.ConflictResolutionAction; @Schema(allOf = { PolicyTO.class }, - subTypes = { PullPolicyTO.class, PushPolicyTO.class }, + subTypes = { InboundPolicyTO.class, PushPolicyTO.class }, discriminatorProperty = "_class") public abstract class ProvisioningPolicyTO extends PolicyTO { diff --git a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/to/ResourceTO.java b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/to/ResourceTO.java index c5a28daf0bd..97d154ad0e7 100644 --- a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/to/ResourceTO.java +++ b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/to/ResourceTO.java @@ -19,9 +19,10 @@ package org.apache.syncope.common.lib.to; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; import jakarta.ws.rs.PathParam; import java.util.ArrayList; -import java.util.EnumSet; +import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; @@ -69,7 +70,7 @@ public class ResourceTO implements EntityTO { private String propagationPolicy; - private String pullPolicy; + private String inboundPolicy; private String pushPolicy; @@ -79,11 +80,17 @@ public class ResourceTO implements EntityTO { private String accessPolicy; - private final List confOverride = new ArrayList<>(); + @JsonProperty + private boolean confOverrideFlag = false; - private boolean overrideCapabilities = false; + @JsonProperty + private List confOverrideValue; - private final Set capabilitiesOverride = EnumSet.noneOf(ConnectorCapability.class); + @JsonProperty + private boolean capabilitiesOverrideFlag = false; + + @JsonProperty + private Set capabilitiesOverrideValue; private final List propagationActions = new ArrayList<>(); @@ -138,6 +145,14 @@ public void setCreateTraceLevel(final TraceLevel createTraceLevel) { this.createTraceLevel = createTraceLevel; } + public TraceLevel getUpdateTraceLevel() { + return updateTraceLevel; + } + + public void setUpdateTraceLevel(final TraceLevel updateTraceLevel) { + this.updateTraceLevel = updateTraceLevel; + } + public TraceLevel getDeleteTraceLevel() { return deleteTraceLevel; } @@ -146,12 +161,12 @@ public void setDeleteTraceLevel(final TraceLevel deleteTraceLevel) { this.deleteTraceLevel = deleteTraceLevel; } - public TraceLevel getUpdateTraceLevel() { - return updateTraceLevel; + public TraceLevel getProvisioningTraceLevel() { + return provisioningTraceLevel; } - public void setUpdateTraceLevel(final TraceLevel updateTraceLevel) { - this.updateTraceLevel = updateTraceLevel; + public void setProvisioningTraceLevel(final TraceLevel provisioningTraceLevel) { + this.provisioningTraceLevel = provisioningTraceLevel; } public String getPasswordPolicy() { @@ -178,12 +193,12 @@ public void setPropagationPolicy(final String propagationPolicy) { this.propagationPolicy = propagationPolicy; } - public String getPullPolicy() { - return pullPolicy; + public String getInboundPolicy() { + return inboundPolicy; } - public void setPullPolicy(final String pullPolicy) { - this.pullPolicy = pullPolicy; + public void setInboundPolicy(final String inboundPolicy) { + this.inboundPolicy = inboundPolicy; } public String getPushPolicy() { @@ -194,14 +209,6 @@ public void setPushPolicy(final String pushPolicy) { this.pushPolicy = pushPolicy; } - public String getProvisionSorter() { - return provisionSorter; - } - - public void setProvisionSorter(final String provisionSorter) { - this.provisionSorter = provisionSorter; - } - public String getAuthPolicy() { return authPolicy; } @@ -218,6 +225,14 @@ public void setAccessPolicy(final String accessPolicy) { this.accessPolicy = accessPolicy; } + public String getProvisionSorter() { + return provisionSorter; + } + + public void setProvisionSorter(final String provisionSorter) { + this.provisionSorter = provisionSorter; + } + @JsonIgnore public Optional getProvision(final String anyType) { return provisions.stream().filter( @@ -237,28 +252,36 @@ public void setOrgUnit(final OrgUnit orgUnit) { this.orgUnit = orgUnit; } - public List getConfOverride() { - return confOverride; - } - - public boolean isOverrideCapabilities() { - return overrideCapabilities; - } - - public void setOverrideCapabilities(final boolean overrideCapabilities) { - this.overrideCapabilities = overrideCapabilities; + @JsonIgnore + public Optional> getConfOverride() { + return confOverrideFlag ? Optional.ofNullable(confOverrideValue) : Optional.empty(); } - public Set getCapabilitiesOverride() { - return capabilitiesOverride; + @JsonIgnore + public void setConfOverride(final Optional> confOverride) { + if (confOverride == null || confOverride.isEmpty()) { + confOverrideFlag = false; + confOverrideValue = null; + } else { + confOverrideFlag = true; + confOverrideValue = new ArrayList<>(confOverride.orElseThrow()); + } } - public TraceLevel getProvisioningTraceLevel() { - return provisioningTraceLevel; + @JsonIgnore + public Optional> getCapabilitiesOverride() { + return capabilitiesOverrideFlag ? Optional.ofNullable(capabilitiesOverrideValue) : Optional.empty(); } - public void setProvisioningTraceLevel(final TraceLevel provisioningTraceLevel) { - this.provisioningTraceLevel = provisioningTraceLevel; + @JsonIgnore + public void setCapabilitiesOverride(final Optional> capabilitiesOverride) { + if (capabilitiesOverride == null || capabilitiesOverride.isEmpty()) { + capabilitiesOverrideFlag = false; + capabilitiesOverrideValue = null; + } else { + capabilitiesOverrideFlag = true; + capabilitiesOverrideValue = new HashSet<>(capabilitiesOverride.orElseThrow()); + } } public List getPropagationActions() { @@ -279,7 +302,6 @@ public boolean equals(final Object obj) { ResourceTO other = (ResourceTO) obj; return new EqualsBuilder(). append(enforceMandatoryCondition, other.enforceMandatoryCondition). - append(overrideCapabilities, other.overrideCapabilities). append(key, other.key). append(connector, other.connector). append(connectorDisplayName, other.connectorDisplayName). @@ -293,12 +315,14 @@ public boolean equals(final Object obj) { append(passwordPolicy, other.passwordPolicy). append(accountPolicy, other.accountPolicy). append(propagationPolicy, other.propagationPolicy). - append(pullPolicy, other.pullPolicy). + append(inboundPolicy, other.inboundPolicy). append(pushPolicy, other.pushPolicy). append(authPolicy, other.authPolicy). append(accessPolicy, other.accessPolicy). - append(confOverride, other.confOverride). - append(capabilitiesOverride, other.capabilitiesOverride). + append(confOverrideFlag, other.confOverrideFlag). + append(confOverrideValue, other.confOverrideValue). + append(capabilitiesOverrideFlag, other.capabilitiesOverrideFlag). + append(capabilitiesOverrideValue, other.capabilitiesOverrideValue). append(propagationActions, other.propagationActions). append(provisionSorter, other.provisionSorter). build(); @@ -321,13 +345,14 @@ public int hashCode() { append(passwordPolicy). append(accountPolicy). append(propagationPolicy). - append(pullPolicy). + append(inboundPolicy). append(pushPolicy). append(authPolicy). append(accessPolicy). - append(confOverride). - append(overrideCapabilities). - append(capabilitiesOverride). + append(confOverrideFlag). + append(confOverrideValue). + append(capabilitiesOverrideFlag). + append(capabilitiesOverrideValue). append(propagationActions). append(provisionSorter). build(); diff --git a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/types/ConnConfProperty.java b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/types/ConnConfProperty.java index b9a931dd297..0a6b176581d 100644 --- a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/types/ConnConfProperty.java +++ b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/types/ConnConfProperty.java @@ -22,12 +22,11 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import java.util.ArrayList; import java.util.List; -import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.syncope.common.lib.BaseBean; -public class ConnConfProperty implements BaseBean, Comparable { +public class ConnConfProperty implements BaseBean { private static final long serialVersionUID = -8391413960221862238L; @@ -59,11 +58,6 @@ public void setOverridable(final boolean overridable) { this.overridable = overridable; } - @Override - public int compareTo(final ConnConfProperty connConfProperty) { - return ObjectUtils.compare(this.getSchema(), connConfProperty.getSchema()); - } - @Override public int hashCode() { return new HashCodeBuilder(). diff --git a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/types/ConnectorCapability.java b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/types/ConnectorCapability.java index 70c9f4170a0..b3263db379f 100644 --- a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/types/ConnectorCapability.java +++ b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/types/ConnectorCapability.java @@ -29,6 +29,7 @@ public enum ConnectorCapability { UPDATE_DELTA, DELETE, SEARCH, - SYNC; + SYNC, + LIVE_SYNC; } diff --git a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/types/IdMImplementationType.java b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/types/IdMImplementationType.java index 16349a93b96..cd29ec7a6f6 100644 --- a/common/idm/lib/src/main/java/org/apache/syncope/common/lib/types/IdMImplementationType.java +++ b/common/idm/lib/src/main/java/org/apache/syncope/common/lib/types/IdMImplementationType.java @@ -27,24 +27,27 @@ public final class IdMImplementationType { public static final String PROPAGATION_ACTIONS = "PROPAGATION_ACTIONS"; - public static final String PULL_ACTIONS = "PULL_ACTIONS"; + public static final String INBOUND_ACTIONS = "INBOUND_ACTIONS"; public static final String PUSH_ACTIONS = "PUSH_ACTIONS"; - public static final String PULL_CORRELATION_RULE = "PULL_CORRELATION_RULE"; + public static final String INBOUND_CORRELATION_RULE = "INBOUND_CORRELATION_RULE"; public static final String PUSH_CORRELATION_RULE = "PUSH_CORRELATION_RULE"; public static final String PROVISION_SORTER = "PROVISION_SORTER"; + public static final String LIVE_SYNC_DELTA_MAPPER = "LIVE_SYNC_DELTA_MAPPER"; + private static final Map VALUES = Map.ofEntries( Pair.of(RECON_FILTER_BUILDER, "org.apache.syncope.core.provisioning.api.pushpull.ReconFilterBuilder"), Pair.of(PROPAGATION_ACTIONS, "org.apache.syncope.core.provisioning.api.propagation.PropagationActions"), - Pair.of(PULL_ACTIONS, "org.apache.syncope.core.provisioning.api.pushpull.PullActions"), + Pair.of(INBOUND_ACTIONS, "org.apache.syncope.core.provisioning.api.pushpull.InboundActions"), Pair.of(PUSH_ACTIONS, "org.apache.syncope.core.provisioning.api.pushpull.PushActions"), - Pair.of(PULL_CORRELATION_RULE, "org.apache.syncope.core.provisioning.api.rules.PullCorrelationRule"), + Pair.of(INBOUND_CORRELATION_RULE, "org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRule"), Pair.of(PUSH_CORRELATION_RULE, "org.apache.syncope.core.provisioning.api.rules.PushCorrelationRule"), - Pair.of(PROVISION_SORTER, "org.apache.syncope.core.provisioning.api.ProvisionSorter")); + Pair.of(PROVISION_SORTER, "org.apache.syncope.core.provisioning.api.ProvisionSorter"), + Pair.of(LIVE_SYNC_DELTA_MAPPER, "org.apache.syncope.core.provisioning.api.LiveSyncDeltaMapper")); public static Map values() { return VALUES; 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 index e929463cd89..7d454e33cbb 100644 --- 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 @@ -63,8 +63,8 @@ public Builder conflictResolutionAction(final ConflictResolutionAction conflictR return this; } - public Builder pullCorrelationRule(final String pullCorrelationRule) { - instance.setPullCorrelationRule(pullCorrelationRule); + public Builder inboundCorrelationRule(final String inboundCorrelationRule) { + instance.setInboundCorrelationRule(inboundCorrelationRule); return this; } } @@ -79,7 +79,7 @@ public Builder pullCorrelationRule(final String pullCorrelationRule) { private ConflictResolutionAction conflictResolutionAction = ConflictResolutionAction.IGNORE; - private String pullCorrelationRule; + private String inboundCorrelationRule; public String getDestinationRealm() { return destinationRealm; @@ -128,12 +128,12 @@ public void setConflictResolutionAction(final ConflictResolutionAction conflictR this.conflictResolutionAction = conflictResolutionAction; } - public String getPullCorrelationRule() { - return pullCorrelationRule; + public String getInboundCorrelationRule() { + return inboundCorrelationRule; } - @QueryParam("pullCorrelationRule") - public void setPullCorrelationRule(final String pullCorrelationRule) { - this.pullCorrelationRule = pullCorrelationRule; + @QueryParam("inboundCorrelationRule") + public void setInboundCorrelationRule(final String inboundCorrelationRule) { + this.inboundCorrelationRule = inboundCorrelationRule; } } diff --git a/common/idrepo/lib/pom.xml b/common/idrepo/lib/pom.xml index e02675d4cfb..b19aa766335 100644 --- a/common/idrepo/lib/pom.xml +++ b/common/idrepo/lib/pom.xml @@ -67,7 +67,7 @@ under the License. com.fasterxml.jackson.datatype jackson-datatype-jsr310 - + org.apache.commons diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/InboundTaskTO.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/InboundTaskTO.java new file mode 100644 index 00000000000..f77380fc460 --- /dev/null +++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/InboundTaskTO.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.common.lib.to; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; + +@Schema(allOf = { ProvisioningTaskTO.class }, + subTypes = { PullTaskTO.class, LiveSyncTaskTO.class }, discriminatorProperty = "_class") +public abstract class InboundTaskTO extends ProvisioningTaskTO { + + private static final long serialVersionUID = -5538466397907585166L; + + @JsonProperty(required = true) + private String destinationRealm; + + private boolean remediation; + + public String getDestinationRealm() { + return destinationRealm; + } + + public void setDestinationRealm(final String destinationRealm) { + this.destinationRealm = destinationRealm; + } + + public boolean isRemediation() { + return remediation; + } + + public void setRemediation(final boolean remediation) { + this.remediation = remediation; + } + + @Override + public int hashCode() { + return new HashCodeBuilder(). + appendSuper(super.hashCode()). + append(destinationRealm). + append(remediation). + 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 InboundTaskTO other = (InboundTaskTO) obj; + return new EqualsBuilder(). + appendSuper(super.equals(obj)). + append(destinationRealm, other.destinationRealm). + append(remediation, other.remediation). + build(); + } +} diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/LiveSyncTaskTO.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/LiveSyncTaskTO.java new file mode 100644 index 00000000000..2868ab03589 --- /dev/null +++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/LiveSyncTaskTO.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.common.lib.to; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.HashMap; +import java.util.Map; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; + +@Schema(allOf = { InboundTaskTO.class }) +public class LiveSyncTaskTO extends InboundTaskTO implements TemplatableTO { + + private static final long serialVersionUID = -2727384303923661649L; + + private String liveSyncDeltaMapper; + + private final Map templates = new HashMap<>(); + + @JacksonXmlProperty(localName = "_class", isAttribute = true) + @JsonProperty("_class") + @Schema(name = "_class", requiredMode = Schema.RequiredMode.REQUIRED, + example = "org.apache.syncope.common.lib.to.LiveSyncTaskTO") + @Override + public String getDiscriminator() { + return getClass().getName(); + } + + public String getLiveSyncDeltaMapper() { + return liveSyncDeltaMapper; + } + + public void setLiveSyncDeltaMapper(final String liveSyncDeltaMapper) { + this.liveSyncDeltaMapper = liveSyncDeltaMapper; + } + + @Override + public Map getTemplates() { + return templates; + } + + @Override + public int hashCode() { + return new HashCodeBuilder(). + appendSuper(super.hashCode()). + append(liveSyncDeltaMapper). + append(templates). + 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 LiveSyncTaskTO other = (LiveSyncTaskTO) obj; + return new EqualsBuilder(). + appendSuper(super.equals(obj)). + append(liveSyncDeltaMapper, other.liveSyncDeltaMapper). + append(templates, other.templates). + build(); + } +} diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/ProvisioningTaskTO.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/ProvisioningTaskTO.java index 1dcdba0890c..dbc0366daf2 100644 --- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/ProvisioningTaskTO.java +++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/ProvisioningTaskTO.java @@ -31,7 +31,7 @@ import org.apache.syncope.common.lib.types.UnmatchingRule; @Schema(allOf = { SchedTaskTO.class }, - subTypes = { PushTaskTO.class, PullTaskTO.class }, discriminatorProperty = "_class") + subTypes = { PushTaskTO.class, InboundTaskTO.class }, discriminatorProperty = "_class") public abstract class ProvisioningTaskTO extends SchedTaskTO { private static final long serialVersionUID = -5722284116974636425L; diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/PullTaskTO.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/PullTaskTO.java index d393512e064..dd0b7af1c2d 100644 --- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/PullTaskTO.java +++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/PullTaskTO.java @@ -27,8 +27,8 @@ import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.syncope.common.lib.types.PullMode; -@Schema(allOf = { ProvisioningTaskTO.class }) -public class PullTaskTO extends ProvisioningTaskTO implements TemplatableTO { +@Schema(allOf = { InboundTaskTO.class }) +public class PullTaskTO extends InboundTaskTO implements TemplatableTO { private static final long serialVersionUID = -2143537546915809017L; @@ -37,11 +37,6 @@ public class PullTaskTO extends ProvisioningTaskTO implements TemplatableTO { private String reconFilterBuilder; - @JsonProperty(required = true) - private String destinationRealm; - - private boolean remediation; - @JacksonXmlProperty(localName = "_class", isAttribute = true) @JsonProperty("_class") @Schema(name = "_class", requiredMode = Schema.RequiredMode.REQUIRED, @@ -69,35 +64,18 @@ public void setReconFilterBuilder(final String reconFilterBuilder) { this.reconFilterBuilder = reconFilterBuilder; } - public String getDestinationRealm() { - return destinationRealm; - } - - public void setDestinationRealm(final String destinationRealm) { - this.destinationRealm = destinationRealm; - } - @Override public Map getTemplates() { return templates; } - public boolean isRemediation() { - return remediation; - } - - public void setRemediation(final boolean remediation) { - this.remediation = remediation; - } - @Override public int hashCode() { return new HashCodeBuilder(). appendSuper(super.hashCode()). append(pullMode). append(reconFilterBuilder). - append(destinationRealm). - append(remediation). + append(templates). build(); } @@ -117,8 +95,7 @@ public boolean equals(final Object obj) { appendSuper(super.equals(obj)). append(pullMode, other.pullMode). append(reconFilterBuilder, other.reconFilterBuilder). - append(destinationRealm, other.destinationRealm). - append(remediation, other.remediation). + append(templates, other.templates). build(); } } diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java index 7ea2ee65f6a..eb30aa01c46 100644 --- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java +++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/ClientExceptionType.java @@ -64,6 +64,7 @@ public enum ClientExceptionType { InvalidRole(Response.Status.BAD_REQUEST), InvalidUser(Response.Status.BAD_REQUEST), InvalidExternalResource(Response.Status.BAD_REQUEST), + InvalidLiveSyncTask(Response.Status.BAD_REQUEST), InvalidPullTask(Response.Status.BAD_REQUEST), InvalidRequest(Response.Status.BAD_REQUEST), InvalidValues(Response.Status.BAD_REQUEST), diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/PolicyType.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/PolicyType.java index 383a790c987..20286e41ae8 100644 --- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/PolicyType.java +++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/PolicyType.java @@ -49,9 +49,9 @@ public enum PolicyType { */ PROPAGATION, /** - * For handling conflicts resolution during pull. + * For handling conflicts resolution during inbound. */ - PULL, + INBOUND, /** * For handling conflicts resolution during push. */ diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/TaskType.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/TaskType.java index 2fc7efd8b52..5dd6e9ee3cb 100644 --- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/TaskType.java +++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/TaskType.java @@ -18,6 +18,7 @@ */ package org.apache.syncope.common.lib.types; +import org.apache.syncope.common.lib.to.LiveSyncTaskTO; import org.apache.syncope.common.lib.to.MacroTaskTO; import org.apache.syncope.common.lib.to.NotificationTaskTO; import org.apache.syncope.common.lib.to.PropagationTaskTO; @@ -31,6 +32,7 @@ public enum TaskType { PROPAGATION(PropagationTaskTO.class), NOTIFICATION(NotificationTaskTO.class), SCHEDULED(SchedTaskTO.class), + LIVE_SYNC(LiveSyncTaskTO.class), PULL(PullTaskTO.class), PUSH(PushTaskTO.class), MACRO(MacroTaskTO.class); @@ -48,6 +50,8 @@ public Class getToClass() { public static TaskType fromTOClass(final Class clazz) { return PushTaskTO.class.isAssignableFrom(clazz) ? TaskType.PUSH + : LiveSyncTaskTO.class.isAssignableFrom(clazz) + ? TaskType.LIVE_SYNC : PullTaskTO.class.isAssignableFrom(clazz) ? TaskType.PULL : NotificationTaskTO.class.isAssignableFrom(clazz) diff --git a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java index 7b5d02c2c33..c68b76f651e 100644 --- a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java +++ b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java @@ -213,7 +213,8 @@ public List buildObjectClassInfo( orElse(connInstanceTO); Set objectClassInfo = connectorManager.createConnector( - connectorManager.buildConnInstanceOverride(actual, connInstanceTO.getConf(), Optional.empty())). + connectorManager.buildConnInstanceOverride( + actual, Optional.of(connInstanceTO.getConf()), Optional.empty())). getObjectClassInfo(); return objectClassInfo.stream().map(info -> { 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 dd64fe0b73c..91f54d09769 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 @@ -737,7 +737,7 @@ public List pull(final CSVPullSpec spec, final InputStream c spec.getKeyColumn(), columns, spec.getConflictResolutionAction(), - spec.getPullCorrelationRule(), + spec.getInboundCorrelationRule(), connector, pullTask, AuthContextUtils.getWho()); diff --git a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java index 86da7079e9b..3c49a5929bd 100644 --- a/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java +++ b/core/idm/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java @@ -459,8 +459,7 @@ public void check(final ResourceTO resourceTO) { connectorManager.buildConnInstanceOverride( connInstanceDataBinder.getConnInstanceTO(connInstance), resourceTO.getConfOverride(), - resourceTO.isOverrideCapabilities() - ? Optional.of(resourceTO.getCapabilitiesOverride()) : Optional.empty())). + resourceTO.getCapabilitiesOverride())). test(); } 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 index e41cf4da86c..09755c801f4 100644 --- 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 @@ -20,15 +20,15 @@ import java.util.Set; import org.apache.syncope.common.lib.policy.AccountRuleConf; +import org.apache.syncope.common.lib.policy.InboundCorrelationRuleConf; 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.ReportConf; import org.apache.syncope.core.provisioning.api.ImplementationLookup; import org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate; import org.apache.syncope.core.provisioning.api.rules.AccountRule; +import org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRule; import org.apache.syncope.core.provisioning.api.rules.PasswordRule; -import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRule; import org.apache.syncope.core.provisioning.api.rules.PushCorrelationRule; public class DummyImplementationLookup implements ImplementationLookup { @@ -63,8 +63,8 @@ public Class getPasswordRuleClass( } @Override - public Class getPullCorrelationRuleClass( - final Class pullCorrelationRuleConfClass) { + public Class getInboundCorrelationRuleClass( + final Class inboundCorrelationRuleConfClass) { return null; } diff --git a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ResourceLogicTest.java b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ResourceLogicTest.java index f20cad6308f..7ce347b7086 100644 --- a/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ResourceLogicTest.java +++ b/core/idm/logic/src/test/java/org/apache/syncope/core/logic/ResourceLogicTest.java @@ -19,7 +19,6 @@ 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.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -28,6 +27,8 @@ import static org.mockito.Mockito.when; import java.util.List; +import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.to.Item; @@ -143,25 +144,15 @@ public void updateChangePurpose() { public void updateChangeOverrideCapabilities() { ResourceTO ldap = logic.read("resource-ldap"); assertNotNull(ldap); - assertFalse(ldap.isOverrideCapabilities()); assertTrue(ldap.getCapabilitiesOverride().isEmpty()); - ldap.getCapabilitiesOverride().add(ConnectorCapability.SEARCH); + ldap.setCapabilitiesOverride(Optional.of(Set.of(ConnectorCapability.SEARCH))); ldap = logic.update(ldap); assertNotNull(ldap); - assertFalse(ldap.isOverrideCapabilities()); - assertEquals(1, ldap.getCapabilitiesOverride().size()); - assertTrue(ldap.getCapabilitiesOverride().contains(ConnectorCapability.SEARCH)); + assertEquals(1, ldap.getCapabilitiesOverride().orElseThrow().size()); + assertTrue(ldap.getCapabilitiesOverride().orElseThrow().contains(ConnectorCapability.SEARCH)); - ldap.setOverrideCapabilities(true); - ldap = logic.update(ldap); - assertNotNull(ldap); - assertTrue(ldap.isOverrideCapabilities()); - assertEquals(1, ldap.getCapabilitiesOverride().size()); - assertTrue(ldap.getCapabilitiesOverride().contains(ConnectorCapability.SEARCH)); - - ldap.getCapabilitiesOverride().clear(); - ldap.setOverrideCapabilities(false); + ldap.setCapabilitiesOverride(Optional.empty()); logic.update(ldap); } diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ImplementationLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ImplementationLogic.java index 617bac94d15..bdebfc375fb 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ImplementationLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ImplementationLogic.java @@ -203,16 +203,16 @@ public void delete(final String type, final String key) { inUse = !resourceDAO.findByPropagationActionsContaining(implementation).isEmpty(); break; - case IdMImplementationType.PULL_ACTIONS: - inUse = !taskDAO.findByPullActions(implementation).isEmpty(); + case IdMImplementationType.INBOUND_ACTIONS: + inUse = !taskDAO.findByInboundActions(implementation).isEmpty(); break; case IdMImplementationType.PUSH_ACTIONS: inUse = !taskDAO.findByPushActions(implementation).isEmpty(); break; - case IdMImplementationType.PULL_CORRELATION_RULE: - inUse = !policyDAO.findByPullCorrelationRule(implementation).isEmpty(); + case IdMImplementationType.INBOUND_CORRELATION_RULE: + inUse = !policyDAO.findByInboundCorrelationRule(implementation).isEmpty(); break; case IdMImplementationType.PUSH_CORRELATION_RULE: diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java index 38efbd926ba..d9e8a64fefd 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java @@ -312,10 +312,10 @@ public List deleteExecutions( @Override protected Triple getReference(final String jobName) { - String key = JobNamer.getReportKeyFromJobName(jobName); - - return reportDAO.findById(key). - map(r -> Triple.of(JobType.REPORT, key, binder.buildRefDesc(r))).orElse(null); + return JobNamer.getReportKeyFromJobName(jobName). + flatMap(reportDAO::findById). + map(r -> Triple.of(JobType.REPORT, r.getKey(), binder.buildRefDesc(r))). + orElse(null); } @PreAuthorize("hasRole('" + IdRepoEntitlement.REPORT_LIST + "')") 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 4a8049a6269..fdd4743992d 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 @@ -331,6 +331,7 @@ protected ExecTO doExecute( break; case SCHEDULED: + case LIVE_SYNC: case PULL: case PUSH: case MACRO: @@ -420,6 +421,7 @@ public T delete(final TaskType type, final String key) { T taskToDelete = binder.getTaskTO(task, taskUtils, true); if (TaskType.SCHEDULED == taskUtils.getType() + || TaskType.LIVE_SYNC == taskUtils.getType() || TaskType.PULL == taskUtils.getType() || TaskType.PUSH == taskUtils.getType() || TaskType.MACRO == taskUtils.getType()) { @@ -523,10 +525,10 @@ public List deleteExecutions( @Override protected Triple getReference(final String jobName) { - String key = JobNamer.getTaskKeyFromJobName(jobName); - - return taskDAO.findById(key).filter(SchedTask.class::isInstance). - map(t -> Triple.of(JobType.TASK, key, binder.buildRefDesc(t))).orElse(null); + return JobNamer.getTaskKeyFromJobName(jobName). + flatMap(taskDAO::findById).filter(SchedTask.class::isInstance). + map(t -> Triple.of(JobType.TASK, t.getKey(), binder.buildRefDesc(t))). + orElse(null); } @PreAuthorize("hasRole('" + IdRepoEntitlement.TASK_LIST + "')") diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java index 5e3d2e1ef4d..fa07898e8dc 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java @@ -28,8 +28,8 @@ import java.util.Set; import java.util.stream.Collectors; import org.apache.syncope.common.lib.policy.AccountRuleConf; +import org.apache.syncope.common.lib.policy.InboundCorrelationRuleConf; 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.ReportConf; import org.apache.syncope.common.lib.types.IdMImplementationType; @@ -46,19 +46,21 @@ import org.apache.syncope.core.provisioning.api.macro.Command; import org.apache.syncope.core.provisioning.api.notification.RecipientsProvider; import org.apache.syncope.core.provisioning.api.propagation.PropagationActions; -import org.apache.syncope.core.provisioning.api.pushpull.PullActions; +import org.apache.syncope.core.provisioning.api.pushpull.InboundActions; import org.apache.syncope.core.provisioning.api.pushpull.PushActions; import org.apache.syncope.core.provisioning.api.pushpull.ReconFilterBuilder; import org.apache.syncope.core.provisioning.api.rules.AccountRule; import org.apache.syncope.core.provisioning.api.rules.AccountRuleConfClass; +import org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRule; +import org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRuleConfClass; import org.apache.syncope.core.provisioning.api.rules.PasswordRule; import org.apache.syncope.core.provisioning.api.rules.PasswordRuleConfClass; -import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRule; -import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRuleConfClass; import org.apache.syncope.core.provisioning.api.rules.PushCorrelationRule; import org.apache.syncope.core.provisioning.api.rules.PushCorrelationRuleConfClass; import org.apache.syncope.core.provisioning.java.data.JEXLItemTransformerImpl; import org.apache.syncope.core.provisioning.java.job.GroupMemberProvisionTaskJobDelegate; +import org.apache.syncope.core.provisioning.java.job.MacroJobDelegate; +import org.apache.syncope.core.provisioning.java.pushpull.LiveSyncJobDelegate; import org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate; import org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate; import org.slf4j.Logger; @@ -86,7 +88,7 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup { private Map, Class> passwordRuleClasses; - private Map, Class> pullCRClasses; + private Map, Class> inboundCRClasses; private Map, Class> pushCRClasses; @@ -127,7 +129,7 @@ public void load() { reportJobDelegateClasses = new HashMap<>(); accountRuleClasses = new HashMap<>(); passwordRuleClasses = new HashMap<>(); - pullCRClasses = new HashMap<>(); + inboundCRClasses = new HashMap<>(); pushCRClasses = new HashMap<>(); for (BeanDefinition bd : scanner.findCandidateComponents(getBasePackage())) { @@ -162,13 +164,14 @@ public void load() { classNames.get(IdRepoImplementationType.PASSWORD_RULE).add(clazz.getName()); passwordRuleClasses.put(annotation.value(), (Class) clazz); } - } else if (PullCorrelationRule.class.isAssignableFrom(clazz)) { - PullCorrelationRuleConfClass annotation = clazz.getAnnotation(PullCorrelationRuleConfClass.class); + } else if (InboundCorrelationRule.class.isAssignableFrom(clazz)) { + InboundCorrelationRuleConfClass annotation = clazz.getAnnotation( + InboundCorrelationRuleConfClass.class); if (annotation == null) { LOG.warn("Found pull correlation rule {} without declared configuration", clazz.getName()); } else { - classNames.get(IdMImplementationType.PULL_CORRELATION_RULE).add(clazz.getName()); - pullCRClasses.put(annotation.value(), (Class) clazz); + classNames.get(IdMImplementationType.INBOUND_CORRELATION_RULE).add(clazz.getName()); + inboundCRClasses.put(annotation.value(), (Class) clazz); } } else if (PushCorrelationRule.class.isAssignableFrom(clazz)) { PushCorrelationRuleConfClass annotation = clazz.getAnnotation(PushCorrelationRuleConfClass.class); @@ -185,7 +188,9 @@ public void load() { } else if (SchedTaskJobDelegate.class.isAssignableFrom(clazz) && !PullJobDelegate.class.isAssignableFrom(clazz) && !PushJobDelegate.class.isAssignableFrom(clazz) - && !GroupMemberProvisionTaskJobDelegate.class.isAssignableFrom(clazz)) { + && !GroupMemberProvisionTaskJobDelegate.class.isAssignableFrom(clazz) + && !MacroJobDelegate.class.isAssignableFrom(clazz) + && !LiveSyncJobDelegate.class.isAssignableFrom(clazz)) { classNames.get(IdRepoImplementationType.TASKJOB_DELEGATE).add(bd.getBeanClassName()); } else if (ReconFilterBuilder.class.isAssignableFrom(clazz)) { @@ -194,8 +199,8 @@ public void load() { classNames.get(IdRepoImplementationType.LOGIC_ACTIONS).add(bd.getBeanClassName()); } else if (PropagationActions.class.isAssignableFrom(clazz)) { classNames.get(IdMImplementationType.PROPAGATION_ACTIONS).add(bd.getBeanClassName()); - } else if (PullActions.class.isAssignableFrom(clazz)) { - classNames.get(IdMImplementationType.PULL_ACTIONS).add(bd.getBeanClassName()); + } else if (InboundActions.class.isAssignableFrom(clazz)) { + classNames.get(IdMImplementationType.INBOUND_ACTIONS).add(bd.getBeanClassName()); } else if (PushActions.class.isAssignableFrom(clazz)) { classNames.get(IdMImplementationType.PUSH_ACTIONS).add(bd.getBeanClassName()); } else if (PlainAttrValueValidator.class.isAssignableFrom(clazz)) { @@ -225,7 +230,7 @@ public void load() { reportJobDelegateClasses = Collections.unmodifiableMap(reportJobDelegateClasses); accountRuleClasses = Collections.unmodifiableMap(accountRuleClasses); passwordRuleClasses = Collections.unmodifiableMap(passwordRuleClasses); - pullCRClasses = Collections.unmodifiableMap(pullCRClasses); + inboundCRClasses = Collections.unmodifiableMap(inboundCRClasses); pushCRClasses = Collections.unmodifiableMap(pushCRClasses); } @@ -254,10 +259,10 @@ public Class getPasswordRuleClass( } @Override - public Class getPullCorrelationRuleClass( - final Class correlationRuleConfClass) { + public Class getInboundCorrelationRuleClass( + final Class inboundCorrelationRuleConfClass) { - return pullCRClasses.get(correlationRuleConfClass); + return inboundCRClasses.get(inboundCorrelationRuleConfClass); } @Override diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/DummyImplementationLookup.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/DummyImplementationLookup.java index e41cf4da86c..8bb53d724d0 100644 --- a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/DummyImplementationLookup.java +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/DummyImplementationLookup.java @@ -20,15 +20,15 @@ import java.util.Set; import org.apache.syncope.common.lib.policy.AccountRuleConf; +import org.apache.syncope.common.lib.policy.InboundCorrelationRuleConf; 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.ReportConf; import org.apache.syncope.core.provisioning.api.ImplementationLookup; import org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate; import org.apache.syncope.core.provisioning.api.rules.AccountRule; +import org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRule; import org.apache.syncope.core.provisioning.api.rules.PasswordRule; -import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRule; import org.apache.syncope.core.provisioning.api.rules.PushCorrelationRule; public class DummyImplementationLookup implements ImplementationLookup { @@ -63,9 +63,8 @@ public Class getPasswordRuleClass( } @Override - public Class getPullCorrelationRuleClass( - final Class pullCorrelationRuleConfClass) { - + public Class getInboundCorrelationRuleClass( + final Class inboundCorrelationRuleConfClass) { return null; } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PolicyDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PolicyDAO.java index 8f07c870459..a8ad66c1cc2 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PolicyDAO.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PolicyDAO.java @@ -23,9 +23,9 @@ import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.Implementation; import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.InboundPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy; import org.apache.syncope.core.persistence.api.entity.policy.Policy; -import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; public interface PolicyDAO extends DAO { @@ -38,7 +38,7 @@ public interface PolicyDAO extends DAO { List findByPasswordRule(Implementation passwordRule); - List findByPullCorrelationRule(Implementation correlationRule); + List findByInboundCorrelationRule(Implementation correlationRule); List findByPushCorrelationRule(Implementation correlationRule); diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/TaskDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/TaskDAO.java index 13092f69358..ce496456f43 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/TaskDAO.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/TaskDAO.java @@ -46,7 +46,7 @@ public interface TaskDAO extends DAO> { List findByReconFilterBuilder(Implementation reconFilterBuilder); - List findByPullActions(Implementation pullActions); + List findByInboundActions(Implementation inboundActions); List findByPushActions(Implementation pushActions); diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnInstance.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnInstance.java index 91cbe9da2a9..f640516337b 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnInstance.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ConnInstance.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.persistence.api.entity; -import java.util.Collection; import java.util.List; import java.util.Set; import org.apache.syncope.common.lib.types.ConnConfProperty; @@ -61,9 +60,9 @@ public interface ConnInstance extends Entity { List getResources(); - void setConf(Collection conf); + void setConf(List conf); - Set getConf(); + List getConf(); void setConnRequestTimeout(Integer timeout); diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ExternalResource.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ExternalResource.java index 95f67463082..af8bc7298e9 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ExternalResource.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/ExternalResource.java @@ -27,9 +27,9 @@ import org.apache.syncope.common.lib.types.ConnectorCapability; import org.apache.syncope.common.lib.types.TraceLevel; import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.InboundPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy; -import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; public interface ExternalResource extends ProvidedKeyEntity { @@ -38,15 +38,13 @@ public interface ExternalResource extends ProvidedKeyEntity { void setConnector(ConnInstance connector); - Set getConfOverride(); + Optional> getConfOverride(); - void setConfOverride(Set confOverride); + void setConfOverride(Optional> confOverride); - boolean isOverrideCapabilities(); + Optional> getCapabilitiesOverride(); - void setOverrideCapabilities(boolean overrideCapabilities); - - Set getCapabilitiesOverride(); + void setCapabilitiesOverride(Optional> capabilitiesOverride); AccountPolicy getAccountPolicy(); @@ -60,18 +58,18 @@ public interface ExternalResource extends ProvidedKeyEntity { void setPropagationPolicy(PropagationPolicy propagationPolicy); - PullPolicy getPullPolicy(); + InboundPolicy getInboundPolicy(); - void setPullPolicy(PullPolicy pullPolicy); + void setInboundPolicy(InboundPolicy inboundPolicy); PushPolicy getPushPolicy(); + void setPushPolicy(PushPolicy pushPolicy); + Implementation getProvisionSorter(); void setProvisionSorter(Implementation provisionSorter); - void setPushPolicy(PushPolicy pushPolicy); - TraceLevel getCreateTraceLevel(); void setCreateTraceLevel(TraceLevel createTraceLevel); diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PullCorrelationRuleEntity.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/InboundCorrelationRuleEntity.java similarity index 83% rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PullCorrelationRuleEntity.java rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/InboundCorrelationRuleEntity.java index fb3f1626cf9..c3f361f109b 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PullCorrelationRuleEntity.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/InboundCorrelationRuleEntity.java @@ -18,9 +18,9 @@ */ package org.apache.syncope.core.persistence.api.entity.policy; -public interface PullCorrelationRuleEntity extends CorrelationRuleEntity { +public interface InboundCorrelationRuleEntity extends CorrelationRuleEntity { - PullPolicy getPullPolicy(); + InboundPolicy getInboundPolicy(); - void setPullPolicy(PullPolicy pullPolicy); + void setInboundPolicy(InboundPolicy inboundPolicy); } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PullPolicy.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/InboundPolicy.java similarity index 77% rename from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PullPolicy.java rename to core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/InboundPolicy.java index 353682fe4fb..1e7dc87458c 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PullPolicy.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/InboundPolicy.java @@ -21,11 +21,11 @@ import java.util.List; import java.util.Optional; -public interface PullPolicy extends ProvisioningPolicy { +public interface InboundPolicy extends ProvisioningPolicy { - boolean add(PullCorrelationRuleEntity rule); + boolean add(InboundCorrelationRuleEntity rule); - Optional getCorrelationRule(String anyType); + Optional getCorrelationRule(String anyType); - List getCorrelationRules(); + List getCorrelationRules(); } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PolicyUtils.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PolicyUtils.java index ddabff76946..2b0536d326f 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PolicyUtils.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PolicyUtils.java @@ -55,8 +55,8 @@ public Class policyClass() { case PROPAGATION: return PropagationPolicy.class; - case PULL: - return PullPolicy.class; + case INBOUND: + return InboundPolicy.class; case PUSH: default: diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PolicyUtilsFactory.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PolicyUtilsFactory.java index e9fba5e3902..655d01a61c3 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PolicyUtilsFactory.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PolicyUtilsFactory.java @@ -22,10 +22,10 @@ import org.apache.syncope.common.lib.policy.AccountPolicyTO; import org.apache.syncope.common.lib.policy.AttrReleasePolicyTO; import org.apache.syncope.common.lib.policy.AuthPolicyTO; +import org.apache.syncope.common.lib.policy.InboundPolicyTO; import org.apache.syncope.common.lib.policy.PasswordPolicyTO; import org.apache.syncope.common.lib.policy.PolicyTO; import org.apache.syncope.common.lib.policy.PropagationPolicyTO; -import org.apache.syncope.common.lib.policy.PullPolicyTO; import org.apache.syncope.common.lib.policy.PushPolicyTO; import org.apache.syncope.common.lib.policy.TicketExpirationPolicyTO; import org.apache.syncope.common.lib.types.PolicyType; @@ -44,8 +44,8 @@ public PolicyUtils getInstance(final Policy policy) { type = PolicyType.PASSWORD; } else if (policy instanceof PropagationPolicy) { type = PolicyType.PROPAGATION; - } else if (policy instanceof PullPolicy) { - type = PolicyType.PULL; + } else if (policy instanceof InboundPolicy) { + type = PolicyType.INBOUND; } else if (policy instanceof PushPolicy) { type = PolicyType.PUSH; } else if (policy instanceof AuthPolicy) { @@ -71,8 +71,8 @@ public PolicyUtils getInstance(final Class policyClass) { type = PolicyType.PASSWORD; } else if (policyClass == PropagationPolicyTO.class) { type = PolicyType.PROPAGATION; - } else if (policyClass == PullPolicyTO.class) { - type = PolicyType.PULL; + } else if (policyClass == InboundPolicyTO.class) { + type = PolicyType.INBOUND; } else if (policyClass == PushPolicyTO.class) { type = PolicyType.PUSH; } else if (policyClass == AuthPolicyTO.class) { diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PushCorrelationRuleEntity.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PushCorrelationRuleEntity.java index 766b8222aba..d77aaff8e15 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PushCorrelationRuleEntity.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PushCorrelationRuleEntity.java @@ -22,5 +22,5 @@ public interface PushCorrelationRuleEntity extends CorrelationRuleEntity { PushPolicy getPushPolicy(); - void setPushPolicy(PushPolicy pullPolicy); + void setPushPolicy(PushPolicy pushPolicy); } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/AnyTemplateLiveSyncTask.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/AnyTemplateLiveSyncTask.java new file mode 100644 index 00000000000..a786c0f4bd9 --- /dev/null +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/AnyTemplateLiveSyncTask.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.api.entity.task; + +import org.apache.syncope.core.persistence.api.entity.AnyTemplate; + +public interface AnyTemplateLiveSyncTask extends AnyTemplate { + + LiveSyncTask getLiveSyncTask(); + + void setLiveSyncTask(LiveSyncTask liveSyncTask); +} diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/InboundTask.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/InboundTask.java new file mode 100644 index 00000000000..3f0b35d2fac --- /dev/null +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/InboundTask.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.api.entity.task; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.entity.AnyTemplate; +import org.apache.syncope.core.persistence.api.entity.Realm; + +public interface InboundTask extends ProvisioningTask { + + Realm getDestinationRealm(); + + void setDestinationRealm(Realm destinationRealm); + + void setRemediation(boolean remediation); + + boolean isRemediation(); + + Optional getTemplate(String anyType); + + List getTemplates(); +} diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/LiveSyncTask.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/LiveSyncTask.java new file mode 100644 index 00000000000..b7df52f53ea --- /dev/null +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/LiveSyncTask.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.api.entity.task; + +import java.util.List; +import java.util.Optional; +import org.apache.syncope.core.persistence.api.entity.Implementation; + +public interface LiveSyncTask extends InboundTask { + + Implementation getLiveSyncDeltaMapper(); + + void setLiveSyncDeltaMapper(Implementation liveSyncDeltaMapper); + + boolean add(AnyTemplateLiveSyncTask template); + + @Override + Optional getTemplate(String anyType); + + @Override + List getTemplates(); +} diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/PullTask.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/PullTask.java index 5468240cba2..ef0208bc7b0 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/PullTask.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/PullTask.java @@ -22,9 +22,8 @@ import java.util.Optional; import org.apache.syncope.common.lib.types.PullMode; import org.apache.syncope.core.persistence.api.entity.Implementation; -import org.apache.syncope.core.persistence.api.entity.Realm; -public interface PullTask extends ProvisioningTask { +public interface PullTask extends InboundTask { PullMode getPullMode(); @@ -34,17 +33,11 @@ public interface PullTask extends ProvisioningTask { void setReconFilterBuilder(Implementation reconFilterBuilder); - Realm getDestinationRealm(); - - void setDestinationRealm(Realm destinationRealm); - boolean add(AnyTemplatePullTask template); + @Override Optional getTemplate(String anyType); + @Override List getTemplates(); - - void setRemediation(boolean remediation); - - boolean isRemediation(); } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/TaskUtils.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/TaskUtils.java index c5c83365594..d5e0e6d46ec 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/TaskUtils.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/TaskUtils.java @@ -29,17 +29,60 @@ public interface TaskUtils { > E newTaskExec(); - T newTaskTO(); + default T newTaskTO() { + @SuppressWarnings("unchecked") + Class taskClass = (Class) getType().getToClass(); + try { + return taskClass.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + return null; + } + } - > Class taskClass(); + @SuppressWarnings("unchecked") + default > Class taskClass() { + Class result = null; - Class taskTOClass(); + switch (getType()) { + case PROPAGATION: + result = (Class) PropagationTask.class; + break; - String getTaskTable(); + case SCHEDULED: + result = (Class) SchedTask.class; + break; + + case LIVE_SYNC: + result = (Class) LiveSyncTask.class; + break; + + case PULL: + result = (Class) PullTask.class; + break; + + case PUSH: + result = (Class) PushTask.class; + break; + + case MACRO: + result = (Class) MacroTask.class; + break; + + case NOTIFICATION: + result = (Class) NotificationTask.class; + break; + + default: + } + + return result; + } + + String getTaskStorage(); Class> getTaskEntity(); - String getTaskExecTable(); + String getTaskExecStorage(); Class> getTaskExecEntity(); } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPolicyDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPolicyDAO.java index dbc0c5b389c..c0bec47c5d0 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPolicyDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPolicyDAO.java @@ -35,10 +35,10 @@ import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy; import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy; import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.InboundPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy; import org.apache.syncope.core.persistence.api.entity.policy.Policy; import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy; -import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy; import org.apache.syncope.core.persistence.jpa.entity.JPAExternalResource; @@ -47,10 +47,10 @@ import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccountPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAttrReleasePolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAuthPolicy; +import org.apache.syncope.core.persistence.jpa.entity.policy.JPAInboundCorrelationRuleEntity; +import org.apache.syncope.core.persistence.jpa.entity.policy.JPAInboundPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPasswordPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPropagationPolicy; -import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPullCorrelationRuleEntity; -import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPullPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPushCorrelationRuleEntity; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPushPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPATicketExpirationPolicy; @@ -68,8 +68,8 @@ protected static Class getEntityRef ? JPAPasswordPolicy.class : PropagationPolicy.class.isAssignableFrom(reference) ? JPAPropagationPolicy.class - : PullPolicy.class.isAssignableFrom(reference) - ? JPAPullPolicy.class + : InboundPolicy.class.isAssignableFrom(reference) + ? JPAInboundPolicy.class : PushPolicy.class.isAssignableFrom(reference) ? JPAPushPolicy.class : AuthPolicy.class.isAssignableFrom(reference) @@ -183,10 +183,10 @@ public List findByPasswordRule(final Implementation passwordRule } @Override - public List findByPullCorrelationRule(final Implementation correlationRule) { - TypedQuery query = entityManager.createQuery( - "SELECT DISTINCT e.pullPolicy FROM " + JPAPullCorrelationRuleEntity.class.getSimpleName() + " e " - + "WHERE e.implementation=:correlationRule", PullPolicy.class); + public List findByInboundCorrelationRule(final Implementation correlationRule) { + TypedQuery query = entityManager.createQuery( + "SELECT DISTINCT e.inboundPolicy FROM " + JPAInboundCorrelationRuleEntity.class.getSimpleName() + " e " + + "WHERE e.implementation=:correlationRule", InboundPolicy.class); query.setParameter("correlationRule", correlationRule); return query.getResultList(); @@ -219,7 +219,7 @@ public

    P save(final P policy) { if (policy instanceof AccountPolicy || policy instanceof PasswordPolicy || policy instanceof PropagationPolicy - || policy instanceof PullPolicy + || policy instanceof InboundPolicy || policy instanceof PushPolicy) { resourceDAO.findByPolicy(policy). @@ -239,8 +239,8 @@ public void delete(final Policy policy) { resourceDAO.findByPolicy(policy).forEach(resource -> resource.setPasswordPolicy(null)); } else if (policy instanceof PropagationPolicy) { resourceDAO.findByPolicy(policy).forEach(resource -> resource.setPropagationPolicy(null)); - } else if (policy instanceof PullPolicy) { - resourceDAO.findByPolicy(policy).forEach(resource -> resource.setPullPolicy(null)); + } else if (policy instanceof InboundPolicy) { + resourceDAO.findByPolicy(policy).forEach(resource -> resource.setInboundPolicy(null)); } else if (policy instanceof PushPolicy) { resourceDAO.findByPolicy(policy).forEach(resource -> resource.setPushPolicy(null)); } else if (policy instanceof AuthPolicy) { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java index 19372eaafb6..500b6ab1fd2 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java @@ -172,11 +172,11 @@ public List findByReconFilterBuilder(final Implementation reconFilterB } @Override - public List findByPullActions(final Implementation pullActions) { + public List findByInboundActions(final Implementation inboundActions) { TypedQuery query = entityManager.createQuery( "SELECT e FROM " + JPAPullTask.class.getSimpleName() + " e " - + "WHERE :pullActions MEMBER OF e.actions", PullTask.class); - query.setParameter("pullActions", pullActions); + + "WHERE :inboundActions MEMBER OF e.actions", PullTask.class); + query.setParameter("inboundActions", inboundActions); return query.getResultList(); } @@ -270,7 +270,10 @@ protected StringBuilder buildFindAllQuery( final List parameters) { if (resource != null - && type != TaskType.PROPAGATION && type != TaskType.PUSH && type != TaskType.PULL) { + && type != TaskType.PROPAGATION + && type != TaskType.LIVE_SYNC + && type != TaskType.PUSH + && type != TaskType.PULL) { throw new IllegalArgumentException(type + " is not related to " + ExternalResource.class.getSimpleName()); } @@ -285,11 +288,11 @@ protected StringBuilder buildFindAllQuery( throw new IllegalArgumentException(type + " is not related to notifications"); } - String taskTable = taskUtilsFactory.getInstance(type).getTaskTable(); + String taskTable = taskUtilsFactory.getInstance(type).getTaskStorage(); StringBuilder queryString = new StringBuilder("SELECT ").append(taskTable).append(".*"); if (orderByTaskExecInfo) { - String taskExecTable = taskUtilsFactory.getInstance(type).getTaskExecTable(); + String taskExecTable = taskUtilsFactory.getInstance(type).getTaskExecStorage(); queryString.append(',').append(taskExecTable).append(".startDate AS startDate"). append(',').append(taskExecTable).append(".endDate AS endDate"). append(',').append(taskExecTable).append(".status AS status"). @@ -435,7 +438,7 @@ public > List findAll( parameters)). append(" AND id NOT IN "). append("(SELECT task_id AS id FROM "). - append(taskUtilsFactory.getInstance(type).getTaskExecTable()). + append(taskUtilsFactory.getInstance(type).getTaskExecStorage()). append(')'). append(")) T"); } else { @@ -485,7 +488,7 @@ public long count( StringBuilder queryString = buildFindAllQuery(type, resource, notification, anyTypeKind, entityKey, false, parameters); - String table = taskUtilsFactory.getInstance(type).getTaskTable(); + String table = taskUtilsFactory.getInstance(type).getTaskStorage(); Query query = entityManager.createNativeQuery(StringUtils.replaceOnce( queryString.toString(), "SELECT " + table + ".*, null AS startDate, null AS endDate, null AS status", diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ExternalResourceRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ExternalResourceRepoExtImpl.java index cbe18ab38c3..b410a45a090 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ExternalResourceRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/ExternalResourceRepoExtImpl.java @@ -33,10 +33,10 @@ import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO; import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.InboundPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy; import org.apache.syncope.core.persistence.api.entity.policy.Policy; import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy; -import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; import org.apache.syncope.core.persistence.jpa.entity.JPAExternalResource; import org.apache.syncope.core.spring.security.AuthContextUtils; @@ -111,8 +111,8 @@ public List findByPolicy(final Policy policy) { queryString.append("passwordPolicy"); } else if (PropagationPolicy.class.isAssignableFrom(policy.getClass())) { queryString.append("propagationPolicy"); - } else if (PullPolicy.class.isAssignableFrom(policy.getClass())) { - queryString.append("pullPolicy"); + } else if (InboundPolicy.class.isAssignableFrom(policy.getClass())) { + queryString.append("inboundPolicy"); } else if (PushPolicy.class.isAssignableFrom(policy.getClass())) { queryString.append("pushPolicy"); } @@ -171,6 +171,7 @@ public void deleteById(final String key) { } taskDAO.deleteAll(resource, TaskType.PROPAGATION); + taskDAO.deleteAll(resource, TaskType.LIVE_SYNC); taskDAO.deleteAll(resource, TaskType.PULL); taskDAO.deleteAll(resource, TaskType.PUSH); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractEntityFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractEntityFactory.java index 9ec154eeb6a..67404affb55 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractEntityFactory.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/AbstractEntityFactory.java @@ -79,15 +79,16 @@ import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy; import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy; import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.InboundCorrelationRuleEntity; +import org.apache.syncope.core.persistence.api.entity.policy.InboundPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy; -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.policy.PushCorrelationRuleEntity; import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy; import org.apache.syncope.core.persistence.api.entity.task.AnyTemplatePullTask; import org.apache.syncope.core.persistence.api.entity.task.FormPropertyDef; +import org.apache.syncope.core.persistence.api.entity.task.LiveSyncTask; import org.apache.syncope.core.persistence.api.entity.task.MacroTask; import org.apache.syncope.core.persistence.api.entity.task.MacroTaskCommand; import org.apache.syncope.core.persistence.api.entity.task.NotificationTask; @@ -136,15 +137,16 @@ import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccountPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAttrReleasePolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAuthPolicy; +import org.apache.syncope.core.persistence.jpa.entity.policy.JPAInboundCorrelationRuleEntity; +import org.apache.syncope.core.persistence.jpa.entity.policy.JPAInboundPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPasswordPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPropagationPolicy; -import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPullCorrelationRuleEntity; -import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPullPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPushCorrelationRuleEntity; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPushPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPATicketExpirationPolicy; import org.apache.syncope.core.persistence.jpa.entity.task.JPAAnyTemplatePullTask; import org.apache.syncope.core.persistence.jpa.entity.task.JPAFormPropertyDef; +import org.apache.syncope.core.persistence.jpa.entity.task.JPALiveSyncTask; import org.apache.syncope.core.persistence.jpa.entity.task.JPAMacroTask; import org.apache.syncope.core.persistence.jpa.entity.task.JPAMacroTaskCommand; import org.apache.syncope.core.persistence.jpa.entity.task.JPANotificationTask; @@ -189,10 +191,10 @@ public E newEntity(final Class reference) { result = (E) new JPAPropagationPolicy(); } else if (reference.equals(PushPolicy.class)) { result = (E) new JPAPushPolicy(); - } else if (reference.equals(PullPolicy.class)) { - result = (E) new JPAPullPolicy(); - } else if (reference.equals(PullCorrelationRuleEntity.class)) { - result = (E) new JPAPullCorrelationRuleEntity(); + } else if (reference.equals(InboundPolicy.class)) { + result = (E) new JPAInboundPolicy(); + } else if (reference.equals(InboundCorrelationRuleEntity.class)) { + result = (E) new JPAInboundCorrelationRuleEntity(); } else if (reference.equals(PushCorrelationRuleEntity.class)) { result = (E) new JPAPushCorrelationRuleEntity(); } else if (reference.equals(AnyTypeClass.class)) { @@ -275,6 +277,8 @@ public E newEntity(final Class reference) { result = (E) new JPAPropagationTask(); } else if (reference.equals(PushTask.class)) { result = (E) new JPAPushTask(); + } else if (reference.equals(LiveSyncTask.class)) { + result = (E) new JPALiveSyncTask(); } else if (reference.equals(PullTask.class)) { result = (E) new JPAPullTask(); } else if (reference.equals(MacroTask.class)) { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstance.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstance.java index 7ebfbb76643..a6c25ac751f 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstance.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAConnInstance.java @@ -34,7 +34,6 @@ import jakarta.persistence.Table; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; -import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Optional; @@ -58,10 +57,14 @@ public class JPAConnInstance extends AbstractGeneratedKeyEntity implements ConnI public static final String TABLE = "ConnInstance"; - protected static final TypeReference> TYPEREF = + protected static final TypeReference> CONNECTOR_CAPABILITY_TYPEREF = new TypeReference>() { }; + protected static final TypeReference> CONN_CONF_PROPS_TYPEREF = + new TypeReference>() { + }; + private static final int DEFAULT_TIMEOUT = 10; @ManyToOne(fetch = FetchType.EAGER, optional = false) @@ -74,25 +77,19 @@ public class JPAConnInstance extends AbstractGeneratedKeyEntity implements ConnI private String location; /** - * Connector bundle class name. - * Within a given location, the triple - * (ConnectorBundle-Name, ConnectorBundle-Version, ConnectorBundle-Version) must be unique. + * Connector class name. */ @NotNull private String connectorName; /** - * Qualified name for the connector bundle. - * Within a given location, the triple - * (ConnectorBundle-Name, ConnectorBundle-Version, ConnectorBundle-Version) must be unique. + * Connector bundle name. */ @NotNull private String bundleName; /** - * Version of the bundle. - * Within a given location, the triple - * (ConnectorBundle-Name, ConnectorBundle-Version, ConnectorBundle-Version) must be unique. + * Connector bundle version. */ @NotNull private String version; @@ -121,8 +118,9 @@ public class JPAConnInstance extends AbstractGeneratedKeyEntity implements ConnI private List resources = new ArrayList<>(); /** - * Connector request timeout. It is not applied in case of sync, full reconciliation and search. - * DEFAULT_TIMEOUT is the default value to be used in case of unspecified timeout. + * Connection request timeout. + * It is not applied in case of sync, full reconciliation and search. + * DEFAULT_TIMEOUT if null. */ private Integer connRequestTimeout = DEFAULT_TIMEOUT; @@ -180,18 +178,15 @@ public void setVersion(final String version) { } @Override - public Set getConf() { - Set configuration = new HashSet<>(); - if (!StringUtils.isBlank(jsonConf)) { - configuration.addAll(List.of(POJOHelper.deserialize(jsonConf, ConnConfProperty[].class))); - } - - return configuration; + public List getConf() { + return StringUtils.isNotBlank(jsonConf) + ? POJOHelper.deserialize(jsonConf, CONN_CONF_PROPS_TYPEREF) + : new ArrayList<>(); } @Override - public void setConf(final Collection conf) { - jsonConf = POJOHelper.serialize(new HashSet<>(conf)); + public void setConf(final List conf) { + jsonConf = POJOHelper.serialize(conf); } @Override @@ -222,9 +217,6 @@ public Set getCapabilities() { @Override public Integer getConnRequestTimeout() { - // DEFAULT_TIMEOUT will be returned in case of null timeout: - // * instances created by the content loader - // * or with a timeout nullified explicitely return Optional.ofNullable(connRequestTimeout).orElse(DEFAULT_TIMEOUT); } @@ -248,7 +240,7 @@ protected void json2list(final boolean clearFirst) { getCapabilities().clear(); } if (capabilities != null) { - getCapabilities().addAll(POJOHelper.deserialize(capabilities, TYPEREF)); + getCapabilities().addAll(POJOHelper.deserialize(capabilities, CONNECTOR_CAPABILITY_TYPEREF)); } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAExternalResource.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAExternalResource.java index 53fce81e02d..f81d7987dce 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAExternalResource.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAExternalResource.java @@ -40,7 +40,6 @@ import jakarta.persistence.UniqueConstraint; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; @@ -55,15 +54,15 @@ import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.Implementation; import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.InboundPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy; -import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; import org.apache.syncope.core.persistence.common.validation.ExternalResourceCheck; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccountPolicy; +import org.apache.syncope.core.persistence.jpa.entity.policy.JPAInboundPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPasswordPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPropagationPolicy; -import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPullPolicy; import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPushPolicy; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; @@ -77,7 +76,11 @@ public class JPAExternalResource extends AbstractProvidedKeyEntity implements Ex public static final String TABLE = "ExternalResource"; - protected static final TypeReference> CAPABILITY_TYPEREF = + protected static final TypeReference> CONN_CONF_PROPS_TYPEREF = + new TypeReference>() { + }; + + protected static final TypeReference> CONNECTOR_CAPABILITY_TYPEREF = new TypeReference>() { }; @@ -128,7 +131,7 @@ public class JPAExternalResource extends AbstractProvidedKeyEntity implements Ex private JPAPropagationPolicy propagationPolicy; @ManyToOne(fetch = FetchType.EAGER) - private JPAPullPolicy pullPolicy; + private JPAInboundPolicy inboundPolicy; @ManyToOne(fetch = FetchType.EAGER) private JPAPushPolicy pushPolicy; @@ -142,15 +145,9 @@ public class JPAExternalResource extends AbstractProvidedKeyEntity implements Ex @Lob private String jsonConf; - @NotNull - private Boolean overrideCapabilities = false; - @Lob private String capabilitiesOverride; - @Transient - private final Set capabilitiesOverrideSet = new HashSet<>(); - @Lob private String provisions; @@ -193,14 +190,12 @@ public void setConnector(final ConnInstance connector) { @Override public Optional getProvisionByAnyType(final String anyType) { - return getProvisions().stream(). - filter(provision -> provision.getAnyType().equals(anyType)).findFirst(); + return getProvisions().stream().filter(provision -> provision.getAnyType().equals(anyType)).findFirst(); } @Override public Optional getProvisionByObjectClass(final String objectClass) { - return getProvisions().stream(). - filter(provision -> provision.getObjectClass().equals(objectClass)).findFirst(); + return getProvisions().stream().filter(provision -> provision.getObjectClass().equals(objectClass)).findFirst(); } @Override @@ -303,14 +298,14 @@ public void setPropagationPolicy(final PropagationPolicy propagationPolicy) { } @Override - public PullPolicy getPullPolicy() { - return pullPolicy; + public InboundPolicy getInboundPolicy() { + return inboundPolicy; } @Override - public void setPullPolicy(final PullPolicy pullPolicy) { - checkType(pullPolicy, JPAPullPolicy.class); - this.pullPolicy = (JPAPullPolicy) pullPolicy; + public void setInboundPolicy(final InboundPolicy inboundPolicy) { + checkType(inboundPolicy, JPAInboundPolicy.class); + this.inboundPolicy = (JPAInboundPolicy) inboundPolicy; } @Override @@ -337,33 +332,31 @@ public void setProvisionSorter(final Implementation provisionSorter) { } @Override - public Set getConfOverride() { - Set confOverride = new HashSet<>(); - if (!StringUtils.isBlank(jsonConf)) { - confOverride.addAll(List.of(POJOHelper.deserialize(jsonConf, ConnConfProperty[].class))); - } - - return confOverride; - } - - @Override - public void setConfOverride(final Set confOverride) { - jsonConf = POJOHelper.serialize(new HashSet<>(confOverride)); + public Optional> getConfOverride() { + return StringUtils.isBlank(jsonConf) + ? Optional.empty() + : Optional.of(POJOHelper.deserialize(jsonConf, CONN_CONF_PROPS_TYPEREF)); } @Override - public boolean isOverrideCapabilities() { - return overrideCapabilities; + public void setConfOverride(final Optional> confOverride) { + confOverride.ifPresentOrElse( + conf -> jsonConf = POJOHelper.serialize(conf), + () -> jsonConf = null); } @Override - public void setOverrideCapabilities(final boolean overrideCapabilities) { - this.overrideCapabilities = overrideCapabilities; + public Optional> getCapabilitiesOverride() { + return StringUtils.isBlank(capabilitiesOverride) + ? Optional.empty() + : Optional.of(POJOHelper.deserialize(capabilitiesOverride, CONNECTOR_CAPABILITY_TYPEREF)); } @Override - public Set getCapabilitiesOverride() { - return capabilitiesOverrideSet; + public void setCapabilitiesOverride(final Optional> capabilitiesOverride) { + capabilitiesOverride.ifPresentOrElse( + override -> this.capabilitiesOverride = POJOHelper.serialize(override), + () -> this.capabilitiesOverride = null); } @Override @@ -381,12 +374,8 @@ public List getPropagationActions() { protected void json2list(final boolean clearFirst) { if (clearFirst) { - getCapabilitiesOverride().clear(); getProvisions().clear(); } - if (capabilitiesOverride != null) { - getCapabilitiesOverride().addAll(POJOHelper.deserialize(capabilitiesOverride, CAPABILITY_TYPEREF)); - } if (provisions != null) { getProvisions().addAll(POJOHelper.deserialize(provisions, PROVISION_TYPEREF)); } @@ -406,7 +395,6 @@ public void postSave() { @PrePersist @PreUpdate public void list2json() { - capabilitiesOverride = POJOHelper.serialize(getCapabilitiesOverride()); provisions = POJOHelper.serialize(getProvisions()); } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPullCorrelationRuleEntity.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAInboundCorrelationRuleEntity.java similarity index 59% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPullCorrelationRuleEntity.java rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAInboundCorrelationRuleEntity.java index a7c5e9c2d97..4da848a251b 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPullCorrelationRuleEntity.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAInboundCorrelationRuleEntity.java @@ -23,34 +23,36 @@ import jakarta.persistence.Table; import jakarta.persistence.UniqueConstraint; import org.apache.syncope.common.lib.types.IdMImplementationType; -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.policy.InboundCorrelationRuleEntity; +import org.apache.syncope.core.persistence.api.entity.policy.InboundPolicy; @Entity -@Table(name = JPAPullCorrelationRuleEntity.TABLE, uniqueConstraints = - @UniqueConstraint(columnNames = { "pullPolicy_id", "anyType_id" })) -public class JPAPullCorrelationRuleEntity extends AbstractCorrelationRuleEntity implements PullCorrelationRuleEntity { +@Table(name = JPAInboundCorrelationRuleEntity.TABLE, uniqueConstraints = + @UniqueConstraint(columnNames = { "inboundPolicy_id", "anyType_id" })) +public class JPAInboundCorrelationRuleEntity + extends AbstractCorrelationRuleEntity + implements InboundCorrelationRuleEntity { private static final long serialVersionUID = 4276417265524083919L; - public static final String TABLE = "PullCorrelationRuleEntity"; + public static final String TABLE = "InboundCorrelationRuleEntity"; @ManyToOne(optional = false) - private JPAPullPolicy pullPolicy; + private JPAInboundPolicy inboundPolicy; @Override protected String getImplementationType() { - return IdMImplementationType.PULL_CORRELATION_RULE; + return IdMImplementationType.INBOUND_CORRELATION_RULE; } @Override - public PullPolicy getPullPolicy() { - return pullPolicy; + public InboundPolicy getInboundPolicy() { + return inboundPolicy; } @Override - public void setPullPolicy(final PullPolicy pullPolicy) { - checkType(pullPolicy, JPAPullPolicy.class); - this.pullPolicy = (JPAPullPolicy) pullPolicy; + public void setInboundPolicy(final InboundPolicy inboundPolicy) { + checkType(inboundPolicy, JPAInboundPolicy.class); + this.inboundPolicy = (JPAInboundPolicy) inboundPolicy; } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPullPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAInboundPolicy.java similarity index 63% rename from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPullPolicy.java rename to core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAInboundPolicy.java index 8cb359ccb02..d0fe311b16a 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPullPolicy.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAInboundPolicy.java @@ -26,34 +26,34 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; -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.policy.InboundCorrelationRuleEntity; +import org.apache.syncope.core.persistence.api.entity.policy.InboundPolicy; @Entity -@Table(name = JPAPullPolicy.TABLE) -public class JPAPullPolicy extends AbstractProvisioningPolicy implements PullPolicy { +@Table(name = JPAInboundPolicy.TABLE) +public class JPAInboundPolicy extends AbstractProvisioningPolicy implements InboundPolicy { private static final long serialVersionUID = -6090413855809521279L; - public static final String TABLE = "PullPolicy"; + public static final String TABLE = "InboundPolicy"; - @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "pullPolicy") - private List correlationRules = new ArrayList<>(); + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "inboundPolicy") + private List correlationRules = new ArrayList<>(); @Override - public boolean add(final PullCorrelationRuleEntity filter) { - checkType(filter, JPAPullCorrelationRuleEntity.class); - return this.correlationRules.add((JPAPullCorrelationRuleEntity) filter); + public boolean add(final InboundCorrelationRuleEntity filter) { + checkType(filter, JPAInboundCorrelationRuleEntity.class); + return this.correlationRules.add((JPAInboundCorrelationRuleEntity) filter); } @Override - public Optional getCorrelationRule(final String anyType) { + public Optional getCorrelationRule(final String anyType) { return correlationRules.stream(). filter(rule -> anyType != null && anyType.equals(rule.getAnyType().getKey())).findFirst(); } @Override - public List getCorrelationRules() { + public List getCorrelationRules() { return correlationRules; } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractInboundTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractInboundTask.java new file mode 100644 index 00000000000..12a613ca209 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractInboundTask.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.entity.task; + +import jakarta.persistence.FetchType; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MappedSuperclass; +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.persistence.api.entity.task.InboundTask; +import org.apache.syncope.core.persistence.jpa.entity.JPARealm; + +@MappedSuperclass +public abstract class AbstractInboundTask> + extends AbstractProvisioningTask implements InboundTask { + + private static final long serialVersionUID = -5948324812406435780L; + + @ManyToOne(fetch = FetchType.EAGER, optional = false) + private JPARealm destinationRealm; + + @NotNull + private Boolean remediation = false; + + @Override + public Realm getDestinationRealm() { + return destinationRealm; + } + + @Override + public void setDestinationRealm(final Realm destinationRealm) { + checkType(destinationRealm, JPARealm.class); + this.destinationRealm = (JPARealm) destinationRealm; + } + + @Override + public void setRemediation(final boolean remediation) { + this.remediation = remediation; + } + + @Override + public boolean isRemediation() { + return concurrentSettings != null ? true : remediation; + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAAnyTemplateLiveSyncTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAAnyTemplateLiveSyncTask.java new file mode 100644 index 00000000000..c5f16a5cdd3 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAAnyTemplateLiveSyncTask.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.entity.task; + +import jakarta.persistence.Entity; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; +import org.apache.syncope.core.persistence.api.entity.task.AnyTemplateLiveSyncTask; +import org.apache.syncope.core.persistence.api.entity.task.LiveSyncTask; +import org.apache.syncope.core.persistence.jpa.entity.AbstractAnyTemplate; + +@Entity +@Table(name = JPAAnyTemplateLiveSyncTask.TABLE, uniqueConstraints = + @UniqueConstraint(columnNames = { "liveSyncTask_id", "anyType_id" })) +public class JPAAnyTemplateLiveSyncTask extends AbstractAnyTemplate implements AnyTemplateLiveSyncTask { + + private static final long serialVersionUID = 3517381731849788407L; + + public static final String TABLE = "AnyTemplateLiveSyncTask"; + + @ManyToOne + private JPALiveSyncTask liveSyncTask; + + @Override + public LiveSyncTask getLiveSyncTask() { + return liveSyncTask; + } + + @Override + public void setLiveSyncTask(final LiveSyncTask liveSyncTask) { + checkType(liveSyncTask, JPALiveSyncTask.class); + this.liveSyncTask = (JPALiveSyncTask) liveSyncTask; + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPALiveSyncTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPALiveSyncTask.java new file mode 100644 index 00000000000..e39eea1cbfe --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPALiveSyncTask.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.entity.task; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinTable; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.apache.syncope.common.lib.types.IdMImplementationType; +import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.persistence.api.entity.task.AnyTemplateLiveSyncTask; +import org.apache.syncope.core.persistence.api.entity.task.LiveSyncTask; +import org.apache.syncope.core.persistence.api.entity.task.SchedTask; +import org.apache.syncope.core.persistence.api.entity.task.TaskExec; +import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation; + +@Entity +@Table(name = JPALiveSyncTask.TABLE, uniqueConstraints = + @UniqueConstraint(columnNames = { "resource_id" })) +public class JPALiveSyncTask extends AbstractInboundTask implements LiveSyncTask { + + private static final long serialVersionUID = 7741318722366524409L; + + public static final String TABLE = "LiveSyncTask"; + + @ManyToOne(fetch = FetchType.EAGER) + private JPAImplementation liveSyncDeltaMapper; + + @ManyToMany(fetch = FetchType.EAGER) + @JoinTable(name = "LiveSyncTaskAction", + joinColumns = + @JoinColumn(name = "task_id"), + inverseJoinColumns = + @JoinColumn(name = "implementation_id"), + uniqueConstraints = + @UniqueConstraint(columnNames = { "task_id", "implementation_id" })) + private List actions = new ArrayList<>(); + + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "liveSyncTask") + private List templates = new ArrayList<>(); + + @OneToMany(targetEntity = JPALiveSyncTaskExec.class, + cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "task") + private List> executions = new ArrayList<>(); + + @Override + public Implementation getLiveSyncDeltaMapper() { + return liveSyncDeltaMapper; + } + + @Override + public void setLiveSyncDeltaMapper(final Implementation liveSyncDeltaMapper) { + checkType(liveSyncDeltaMapper, JPAImplementation.class); + checkImplementationType(liveSyncDeltaMapper, IdMImplementationType.LIVE_SYNC_DELTA_MAPPER); + this.liveSyncDeltaMapper = (JPAImplementation) liveSyncDeltaMapper; + } + + @Override + public boolean add(final Implementation action) { + checkType(action, JPAImplementation.class); + checkImplementationType(action, IdMImplementationType.INBOUND_ACTIONS); + return actions.contains((JPAImplementation) action) || actions.add((JPAImplementation) action); + } + + @Override + public List getActions() { + return actions; + } + + @Override + public boolean add(final AnyTemplateLiveSyncTask template) { + checkType(template, JPAAnyTemplateLiveSyncTask.class); + return this.templates.add((JPAAnyTemplateLiveSyncTask) template); + } + + @Override + public Optional getTemplate(final String anyType) { + return templates.stream(). + filter(template -> anyType != null && anyType.equals(template.getAnyType().getKey())). + findFirst(); + } + + @Override + public List getTemplates() { + return templates; + } + + @Override + protected Class> executionClass() { + return JPALiveSyncTaskExec.class; + } + + @Override + protected List> executions() { + return executions; + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPALiveSyncTaskExec.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPALiveSyncTaskExec.java new file mode 100644 index 00000000000..4b4278dcdaa --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPALiveSyncTaskExec.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.entity.task; + +import jakarta.persistence.Entity; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import org.apache.syncope.core.persistence.api.entity.task.LiveSyncTask; +import org.apache.syncope.core.persistence.api.entity.task.SchedTask; +import org.apache.syncope.core.persistence.api.entity.task.TaskExec; + +@Entity +@Table(name = JPALiveSyncTaskExec.TABLE) +public class JPALiveSyncTaskExec extends AbstractTaskExec implements TaskExec { + + private static final long serialVersionUID = 1909033231464074554L; + + public static final String TABLE = "LiveSyncTaskExec"; + + @ManyToOne(optional = false) + private JPALiveSyncTask task; + + @Override + public LiveSyncTask getTask() { + return task; + } + + @Override + public void setTask(final SchedTask task) { + checkType(task, LiveSyncTask.class); + this.task = (JPALiveSyncTask) task; + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTask.java index 0364adc9104..f67c93a87d8 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTask.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPullTask.java @@ -26,7 +26,6 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinTable; import jakarta.persistence.ManyToMany; -import jakarta.persistence.ManyToOne; import jakarta.persistence.OneToMany; import jakarta.persistence.OneToOne; import jakarta.persistence.Table; @@ -38,17 +37,15 @@ import org.apache.syncope.common.lib.types.IdMImplementationType; import org.apache.syncope.common.lib.types.PullMode; import org.apache.syncope.core.persistence.api.entity.Implementation; -import org.apache.syncope.core.persistence.api.entity.Realm; import org.apache.syncope.core.persistence.api.entity.task.AnyTemplatePullTask; import org.apache.syncope.core.persistence.api.entity.task.PullTask; import org.apache.syncope.core.persistence.api.entity.task.SchedTask; import org.apache.syncope.core.persistence.api.entity.task.TaskExec; import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation; -import org.apache.syncope.core.persistence.jpa.entity.JPARealm; @Entity @Table(name = JPAPullTask.TABLE) -public class JPAPullTask extends AbstractProvisioningTask implements PullTask { +public class JPAPullTask extends AbstractInboundTask implements PullTask { private static final long serialVersionUID = -4141057723006682563L; @@ -61,9 +58,6 @@ public class JPAPullTask extends AbstractProvisioningTask implements P @OneToOne private JPAImplementation reconFilterBuilder; - @ManyToOne(fetch = FetchType.EAGER, optional = false) - private JPARealm destinationRealm; - @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "PullTaskAction", joinColumns = @@ -81,9 +75,6 @@ public class JPAPullTask extends AbstractProvisioningTask implements P cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "task") private List> executions = new ArrayList<>(); - @NotNull - private Boolean remediation = false; - @Override public PullMode getPullMode() { return pullMode; @@ -106,21 +97,10 @@ public void setReconFilterBuilder(final Implementation reconFilterBuilder) { this.reconFilterBuilder = (JPAImplementation) reconFilterBuilder; } - @Override - public Realm getDestinationRealm() { - return destinationRealm; - } - - @Override - public void setDestinationRealm(final Realm destinationRealm) { - checkType(destinationRealm, JPARealm.class); - this.destinationRealm = (JPARealm) destinationRealm; - } - @Override public boolean add(final Implementation action) { checkType(action, JPAImplementation.class); - checkImplementationType(action, IdMImplementationType.PULL_ACTIONS); + checkImplementationType(action, IdMImplementationType.INBOUND_ACTIONS); return actions.contains((JPAImplementation) action) || actions.add((JPAImplementation) action); } @@ -147,16 +127,6 @@ public List getTemplates() { return templates; } - @Override - public void setRemediation(final boolean remediation) { - this.remediation = remediation; - } - - @Override - public boolean isRemediation() { - return concurrentSettings != null ? true : remediation; - } - @Override protected Class> executionClass() { return JPAPullTaskExec.class; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskUtils.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskUtils.java index 42276473fa5..88fad872b3f 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskUtils.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskUtils.java @@ -18,20 +18,7 @@ */ package org.apache.syncope.core.persistence.jpa.entity.task; -import org.apache.syncope.common.lib.to.MacroTaskTO; -import org.apache.syncope.common.lib.to.NotificationTaskTO; -import org.apache.syncope.common.lib.to.PropagationTaskTO; -import org.apache.syncope.common.lib.to.PullTaskTO; -import org.apache.syncope.common.lib.to.PushTaskTO; -import org.apache.syncope.common.lib.to.SchedTaskTO; -import org.apache.syncope.common.lib.to.TaskTO; import org.apache.syncope.common.lib.types.TaskType; -import org.apache.syncope.core.persistence.api.entity.task.MacroTask; -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.PullTask; -import org.apache.syncope.core.persistence.api.entity.task.PushTask; -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; import org.apache.syncope.core.persistence.api.entity.task.TaskUtils; @@ -51,41 +38,6 @@ public TaskType getType() { return type; } - @Override - public > Class taskClass() { - Class result = null; - - switch (type) { - case PROPAGATION: - result = (Class) PropagationTask.class; - break; - - case SCHEDULED: - result = (Class) SchedTask.class; - break; - - case PULL: - result = (Class) PullTask.class; - break; - - case PUSH: - result = (Class) PushTask.class; - break; - - case MACRO: - result = (Class) MacroTask.class; - break; - - case NOTIFICATION: - result = (Class) NotificationTask.class; - break; - - default: - } - - return result; - } - @Override public > T newTask() { T result = null; @@ -99,6 +51,10 @@ public > T newTask() { result = (T) new JPASchedTask(); break; + case LIVE_SYNC: + result = (T) new JPALiveSyncTask(); + break; + case PULL: result = (T) new JPAPullTask(); break; @@ -138,6 +94,10 @@ public > E newTaskExec() { result = (E) new JPAPropagationTaskExec(); break; + case LIVE_SYNC: + result = (E) new JPALiveSyncTaskExec(); + break; + case PULL: result = (E) new JPAPullTaskExec(); break; @@ -166,52 +126,7 @@ public > E newTaskExec() { } @Override - public Class taskTOClass() { - Class result = null; - - switch (type) { - case PROPAGATION: - result = (Class) PropagationTaskTO.class; - break; - - case SCHEDULED: - result = (Class) SchedTaskTO.class; - break; - - case PULL: - result = (Class) PullTaskTO.class; - break; - - case PUSH: - result = (Class) PushTaskTO.class; - break; - - case MACRO: - result = (Class) MacroTaskTO.class; - break; - - case NOTIFICATION: - result = (Class) NotificationTaskTO.class; - break; - - default: - } - - return result; - } - - @Override - public T newTaskTO() { - Class taskClass = taskTOClass(); - try { - return taskClass == null ? null : taskClass.getDeclaredConstructor().newInstance(); - } catch (Exception e) { - return null; - } - } - - @Override - public String getTaskTable() { + public String getTaskStorage() { String result = null; switch (type) { @@ -227,6 +142,10 @@ public String getTaskTable() { result = JPAPushTask.TABLE; break; + case LIVE_SYNC: + result = JPALiveSyncTask.TABLE; + break; + case PULL: result = JPAPullTask.TABLE; break; @@ -262,6 +181,10 @@ public Class> getTaskEntity() { result = JPAPushTask.class; break; + case LIVE_SYNC: + result = JPALiveSyncTask.class; + break; + case PULL: result = JPAPullTask.class; break; @@ -281,7 +204,7 @@ public Class> getTaskEntity() { } @Override - public String getTaskExecTable() { + public String getTaskExecStorage() { String result = null; switch (type) { @@ -301,6 +224,10 @@ public String getTaskExecTable() { result = JPAPushTaskExec.TABLE; break; + case LIVE_SYNC: + result = JPALiveSyncTaskExec.TABLE; + break; + case PULL: result = JPAPullTaskExec.TABLE; break; @@ -336,6 +263,10 @@ public Class> getTaskExecEntity() { result = JPAPushTaskExec.class; break; + case LIVE_SYNC: + result = JPALiveSyncTaskExec.class; + break; + case PULL: result = JPAPullTaskExec.class; break; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskUtilsFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskUtilsFactory.java index 35cce33c29a..aaefaba3ac6 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskUtilsFactory.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskUtilsFactory.java @@ -20,6 +20,7 @@ import java.util.HashMap; import java.util.Map; +import org.apache.syncope.common.lib.to.LiveSyncTaskTO; import org.apache.syncope.common.lib.to.MacroTaskTO; import org.apache.syncope.common.lib.to.NotificationTaskTO; import org.apache.syncope.common.lib.to.PropagationTaskTO; @@ -28,6 +29,7 @@ import org.apache.syncope.common.lib.to.SchedTaskTO; import org.apache.syncope.common.lib.to.TaskTO; import org.apache.syncope.common.lib.types.TaskType; +import org.apache.syncope.core.persistence.api.entity.task.LiveSyncTask; import org.apache.syncope.core.persistence.api.entity.task.MacroTask; import org.apache.syncope.core.persistence.api.entity.task.NotificationTask; import org.apache.syncope.core.persistence.api.entity.task.PropagationTask; @@ -61,7 +63,9 @@ public TaskUtils getInstance(final TaskType type) { @Override public TaskUtils getInstance(final Task task) { TaskType type; - if (task instanceof PullTask) { + if (task instanceof LiveSyncTask) { + type = TaskType.LIVE_SYNC; + } else if (task instanceof PullTask) { type = TaskType.PULL; } else if (task instanceof PushTask) { type = TaskType.PUSH; @@ -89,6 +93,8 @@ public TaskUtils getInstance(final Class taskClass) { type = TaskType.NOTIFICATION; } else if (taskClass == SchedTaskTO.class) { type = TaskType.SCHEDULED; + } else if (taskClass == LiveSyncTaskTO.class) { + type = TaskType.LIVE_SYNC; } else if (taskClass == PullTaskTO.class) { type = TaskType.PULL; } else if (taskClass == PushTaskTO.class) { diff --git a/core/persistence-jpa/src/main/resources/domains/MasterContent.xml b/core/persistence-jpa/src/main/resources/domains/MasterContent.xml index 256e121b740..9cc08744063 100644 --- a/core/persistence-jpa/src/main/resources/domains/MasterContent.xml +++ b/core/persistence-jpa/src/main/resources/domains/MasterContent.xml @@ -41,7 +41,8 @@ under the License. - + confOverride, - final Optional> capabilitiesOverride) { + final Optional> confOverride, + final Optional> capabilitiesOverride) { return null; } diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyImplementationLookup.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyImplementationLookup.java index 32eabecea78..d18a89a1545 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyImplementationLookup.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyImplementationLookup.java @@ -20,15 +20,15 @@ import java.util.Set; import org.apache.syncope.common.lib.policy.AccountRuleConf; +import org.apache.syncope.common.lib.policy.InboundCorrelationRuleConf; 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.ReportConf; import org.apache.syncope.core.provisioning.api.ImplementationLookup; import org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate; import org.apache.syncope.core.provisioning.api.rules.AccountRule; +import org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRule; import org.apache.syncope.core.provisioning.api.rules.PasswordRule; -import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRule; import org.apache.syncope.core.provisioning.api.rules.PushCorrelationRule; import org.apache.syncope.core.spring.policy.DefaultAccountRule; import org.apache.syncope.core.spring.policy.DefaultPasswordRule; @@ -65,8 +65,8 @@ public Class getPasswordRuleClass( } @Override - public Class getPullCorrelationRuleClass( - final Class pullCorrelationRuleConfClass) { + public Class getInboundCorrelationRuleClass( + final Class inboundCorrelationRuleConfClass) { return null; } diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ConnInstanceTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ConnInstanceTest.java index 7e185f919a6..48341d28bdb 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ConnInstanceTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ConnInstanceTest.java @@ -25,9 +25,8 @@ import static org.junit.jupiter.api.Assertions.fail; import java.io.File; -import java.util.HashSet; +import java.util.ArrayList; import java.util.List; -import java.util.Set; import java.util.stream.Collectors; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.types.ConnConfPropSchema; @@ -108,7 +107,7 @@ public void save() throws ClassNotFoundException { connInstance.setConnRequestTimeout(60); // set the connector configuration using PropertyTO - Set conf = new HashSet<>(); + List conf = new ArrayList<>(); ConnConfPropSchema endpointSchema = new ConnConfPropSchema(); endpointSchema.setName("endpoint"); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ImplementationTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ImplementationTest.java index 05332decfe2..f1f78f26097 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ImplementationTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ImplementationTest.java @@ -51,16 +51,16 @@ public void findAll() { List implementations = implementationDAO.findAll(); assertFalse(implementations.isEmpty()); - assertEquals(21, implementations.size()); + assertEquals(22, implementations.size()); - implementations = implementationDAO.findByType(IdMImplementationType.PULL_ACTIONS); + implementations = implementationDAO.findByType(IdMImplementationType.INBOUND_ACTIONS); assertEquals(1, implementations.size()); implementations = implementationDAO.findByType(IdMImplementationType.PROPAGATION_ACTIONS); assertEquals(2, implementations.size()); implementations = implementationDAO.findByType(IdRepoImplementationType.TASKJOB_DELEGATE); - assertEquals(7, implementations.size()); + assertEquals(8, implementations.size()); implementations = implementationDAO.findByType(IdRepoImplementationType.REPORT_DELEGATE); assertEquals(1, implementations.size()); @@ -76,7 +76,8 @@ public void findAll() { implementations = implementationDAO.findByType(IdRepoImplementationType.DROPDOWN_VALUE_PROVIDER); assertEquals(1, implementations.size()); - implementations = implementationDAO.findByType(IdMImplementationType.PULL_CORRELATION_RULE); + + implementations = implementationDAO.findByType(IdMImplementationType.INBOUND_CORRELATION_RULE); assertEquals(1, implementations.size()); implementations = implementationDAO.findByType(IdMImplementationType.PUSH_CORRELATION_RULE); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java index 3dba860060a..92ced7698e8 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java @@ -27,8 +27,8 @@ import org.apache.syncope.common.lib.policy.DefaultAccessPolicyConf; import org.apache.syncope.common.lib.policy.DefaultAttrReleasePolicyConf; import org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf; +import org.apache.syncope.common.lib.policy.DefaultInboundCorrelationRuleConf; import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf; -import org.apache.syncope.common.lib.policy.DefaultPullCorrelationRuleConf; import org.apache.syncope.common.lib.policy.DefaultPushCorrelationRuleConf; import org.apache.syncope.common.lib.policy.DefaultTicketExpirationPolicyConf; import org.apache.syncope.common.lib.types.AnyTypeKind; @@ -45,16 +45,16 @@ import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy; import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy; import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.InboundCorrelationRuleEntity; +import org.apache.syncope.core.persistence.api.entity.policy.InboundPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy; import org.apache.syncope.core.persistence.api.entity.policy.Policy; import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy; -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.policy.PushCorrelationRuleEntity; import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy; import org.apache.syncope.core.persistence.jpa.AbstractTest; -import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRule; +import org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRule; import org.apache.syncope.core.provisioning.api.rules.PushCorrelationRule; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; import org.junit.jupiter.api.Test; @@ -75,15 +75,15 @@ public class PolicyTest extends AbstractTest { @Test public void findAll() { - assertEquals(16, policyDAO.findAll().size()); + assertEquals(17, policyDAO.findAll().size()); assertEquals(1, policyDAO.findAll(AccessPolicy.class).size()); assertEquals(2, policyDAO.findAll(AccountPolicy.class).size()); assertEquals(2, policyDAO.findAll(AttrReleasePolicy.class).size()); assertEquals(2, policyDAO.findAll(AuthPolicy.class).size()); assertEquals(3, policyDAO.findAll(PasswordPolicy.class).size()); - assertEquals(1, policyDAO.findAll(PropagationPolicy.class).size()); - assertEquals(4, policyDAO.findAll(PullPolicy.class).size()); + assertEquals(2, policyDAO.findAll(PropagationPolicy.class).size()); + assertEquals(4, policyDAO.findAll(InboundPolicy.class).size()); assertEquals(1, policyDAO.findAll(PushPolicy.class).size()); assertEquals(0, policyDAO.findAll(TicketExpirationPolicy.class).size()); } @@ -96,12 +96,12 @@ public void findByKey() { assertEquals("10000", propagationPolicy.getBackOffParams()); assertEquals(5, propagationPolicy.getMaxAttempts()); - PullPolicy pullPolicy = policyDAO.findById( - "880f8553-069b-4aed-9930-2cd53873f544", PullPolicy.class).orElseThrow(); + InboundPolicy inboundPolicy = policyDAO.findById("880f8553-069b-4aed-9930-2cd53873f544", InboundPolicy.class). + orElseThrow(); - PullCorrelationRuleEntity pullCR = pullPolicy.getCorrelationRule(AnyTypeKind.USER.name()).orElseThrow(); - DefaultPullCorrelationRuleConf pullCRConf = - POJOHelper.deserialize(pullCR.getImplementation().getBody(), DefaultPullCorrelationRuleConf.class); + InboundCorrelationRuleEntity pullCR = inboundPolicy.getCorrelationRule(AnyTypeKind.USER.name()).orElseThrow(); + DefaultInboundCorrelationRuleConf pullCRConf = + POJOHelper.deserialize(pullCR.getImplementation().getBody(), DefaultInboundCorrelationRuleConf.class); assertNotNull(pullCRConf); assertEquals(2, pullCRConf.getSchemas().size()); assertTrue(pullCRConf.getSchemas().contains("username")); @@ -152,9 +152,9 @@ public void createPropagation() { @Test public void createPull() { - PullPolicy pullPolicy = entityFactory.newEntity(PullPolicy.class); - pullPolicy.setConflictResolutionAction(ConflictResolutionAction.IGNORE); - pullPolicy.setName("Pull policy"); + InboundPolicy inboundPolicy = entityFactory.newEntity(InboundPolicy.class); + inboundPolicy.setConflictResolutionAction(ConflictResolutionAction.IGNORE); + inboundPolicy.setName("Pull policy"); final String pullURuleName = "net.tirasa.pull.correlation.TirasaURule"; final String pullGRuleName = "net.tirasa.pull.correlation.TirasaGRule"; @@ -162,36 +162,36 @@ public void createPull() { Implementation impl1 = entityFactory.newEntity(Implementation.class); impl1.setKey(pullURuleName); impl1.setEngine(ImplementationEngine.JAVA); - impl1.setType(IdMImplementationType.PULL_CORRELATION_RULE); - impl1.setBody(PullCorrelationRule.class.getName()); + impl1.setType(IdMImplementationType.INBOUND_CORRELATION_RULE); + impl1.setBody(InboundCorrelationRule.class.getName()); impl1 = implementationDAO.save(impl1); - PullCorrelationRuleEntity rule1 = entityFactory.newEntity(PullCorrelationRuleEntity.class); + InboundCorrelationRuleEntity rule1 = entityFactory.newEntity(InboundCorrelationRuleEntity.class); rule1.setAnyType(anyTypeDAO.getUser()); - rule1.setPullPolicy(pullPolicy); + rule1.setInboundPolicy(inboundPolicy); rule1.setImplementation(impl1); - pullPolicy.add(rule1); + inboundPolicy.add(rule1); Implementation impl2 = entityFactory.newEntity(Implementation.class); impl2.setKey(pullGRuleName); impl2.setEngine(ImplementationEngine.JAVA); - impl2.setType(IdMImplementationType.PULL_CORRELATION_RULE); - impl2.setBody(PullCorrelationRule.class.getName()); + impl2.setType(IdMImplementationType.INBOUND_CORRELATION_RULE); + impl2.setBody(InboundCorrelationRule.class.getName()); impl2 = implementationDAO.save(impl2); - PullCorrelationRuleEntity rule2 = entityFactory.newEntity(PullCorrelationRuleEntity.class); + InboundCorrelationRuleEntity rule2 = entityFactory.newEntity(InboundCorrelationRuleEntity.class); rule2.setAnyType(anyTypeDAO.getGroup()); - rule2.setPullPolicy(pullPolicy); + rule2.setInboundPolicy(inboundPolicy); rule2.setImplementation(impl2); - pullPolicy.add(rule2); + inboundPolicy.add(rule2); - pullPolicy = policyDAO.save(pullPolicy); + inboundPolicy = policyDAO.save(inboundPolicy); - assertNotNull(pullPolicy); + assertNotNull(inboundPolicy); assertEquals(pullURuleName, - pullPolicy.getCorrelationRule(AnyTypeKind.USER.name()).get().getImplementation().getKey()); + inboundPolicy.getCorrelationRule(AnyTypeKind.USER.name()).get().getImplementation().getKey()); assertEquals(pullGRuleName, - pullPolicy.getCorrelationRule(AnyTypeKind.GROUP.name()).get().getImplementation().getKey()); + inboundPolicy.getCorrelationRule(AnyTypeKind.GROUP.name()).get().getImplementation().getKey()); } @Test diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java index 42f6fe74470..6946763aaeb 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/TaskTest.java @@ -48,6 +48,7 @@ import org.apache.syncope.core.persistence.api.dao.TaskExecDAO; import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.persistence.api.entity.task.LiveSyncTask; import org.apache.syncope.core.persistence.api.entity.task.MacroTask; import org.apache.syncope.core.persistence.api.entity.task.MacroTaskCommand; import org.apache.syncope.core.persistence.api.entity.task.PropagationData; @@ -59,7 +60,7 @@ import org.apache.syncope.core.persistence.api.entity.task.TaskExec; import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory; import org.apache.syncope.core.persistence.jpa.AbstractTest; -import org.apache.syncope.core.provisioning.api.pushpull.PullActions; +import org.apache.syncope.core.provisioning.api.pushpull.InboundActions; import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.AttributeBuilder; import org.junit.jupiter.api.Test; @@ -259,16 +260,16 @@ public void savePullTask() { task.setMatchingRule(MatchingRule.UPDATE); task.setUnmatchingRule(UnmatchingRule.PROVISION); - // now adding PullActions - Implementation pullActions = entityFactory.newEntity(Implementation.class); - pullActions.setKey("PullActions" + UUID.randomUUID().toString()); - pullActions.setEngine(ImplementationEngine.JAVA); - pullActions.setType(IdMImplementationType.PULL_ACTIONS); - pullActions.setBody(PullActions.class.getName()); - pullActions = implementationDAO.save(pullActions); + // now adding InboundActions + Implementation inboundActions = entityFactory.newEntity(Implementation.class); + inboundActions.setKey("InboundActions" + UUID.randomUUID().toString()); + inboundActions.setEngine(ImplementationEngine.JAVA); + inboundActions.setType(IdMImplementationType.INBOUND_ACTIONS); + inboundActions.setBody(InboundActions.class.getName()); + inboundActions = implementationDAO.save(inboundActions); entityManager.flush(); - task.add(pullActions); + task.add(inboundActions); // this save() fails because of an invalid Cron Expression try { @@ -296,6 +297,28 @@ public void savePullTask() { assertEquals(task, actual); } + @Test + public void saveLiveSyncTask() { + LiveSyncTask task = entityFactory.newEntity(LiveSyncTask.class); + task.setName("saveLiveSyncTask"); + task.setDescription("LiveSyncTask description"); + task.setActive(true); + task.setJobDelegate(implementationDAO.findById("LiveSyncJobDelegate").orElseThrow()); + task.setDestinationRealm(realmDAO.getRoot()); + task.setCronExpression("BLA BLA"); + task.setMatchingRule(MatchingRule.UPDATE); + task.setUnmatchingRule(UnmatchingRule.PROVISION); + task.setCronExpression(null); + task.setResource(resourceDAO.findById("ws-target-resource-1").orElseThrow()); + + task = taskDAO.save(task); + entityManager.flush(); + assertNotNull(task); + + LiveSyncTask actual = (LiveSyncTask) taskDAO.findById(TaskType.LIVE_SYNC, task.getKey()).orElseThrow(); + assertEquals(task, actual); + } + @Test public void saveMacroTaskSameCommandMultipleOccurrencies() { MacroTask task = entityFactory.newEntity(MacroTask.class); @@ -335,7 +358,7 @@ public void saveMacroTaskSameCommandMultipleOccurrencies() { macroTaskCommand3.setMacroTask(task); task.add(macroTaskCommand3); - task = (MacroTask) taskDAO.save(task); + task = taskDAO.save(task); assertNotNull(task); assertEquals(3, task.getCommands().size()); assertEquals(command1, task.getCommands().get(0).getCommand()); @@ -351,12 +374,12 @@ public void issueSYNCOPE144() { ExternalResource resource = resourceDAO.findById("ws-target-resource-1").orElseThrow(); assertNotNull(resource); - Implementation pullActions = entityFactory.newEntity(Implementation.class); - pullActions.setKey("syncope144"); - pullActions.setEngine(ImplementationEngine.JAVA); - pullActions.setType(IdMImplementationType.PULL_ACTIONS); - pullActions.setBody(PullActions.class.getName()); - pullActions = implementationDAO.save(pullActions); + Implementation inboundActions = entityFactory.newEntity(Implementation.class); + inboundActions.setKey("syncope144"); + inboundActions.setEngine(ImplementationEngine.JAVA); + inboundActions.setType(IdMImplementationType.INBOUND_ACTIONS); + inboundActions.setBody(InboundActions.class.getName()); + inboundActions = implementationDAO.save(inboundActions); PullTask task = entityFactory.newEntity(PullTask.class); @@ -365,7 +388,7 @@ public void issueSYNCOPE144() { task.setDescription("issueSYNCOPE144 Description"); task.setActive(true); task.setPullMode(PullMode.FULL_RECONCILIATION); - task.add(pullActions); + task.add(inboundActions); task.setMatchingRule(MatchingRule.UPDATE); task.setUnmatchingRule(UnmatchingRule.PROVISION); diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml index 76c056f9b1c..68dd1a7966b 100644 --- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml +++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml @@ -45,6 +45,8 @@ under the License. + - - - - - - - + + + + + + + @@ -559,58 +561,68 @@ under the License. jsonconf="[{"schema":{"name":"authenticateScript","displayName":"authenticateScript","helpMessage":"authenticateScript","type":"java.lang.String","required":false,"order":6,"confidential":false,"defaultValues":[""]},"overridable":false,"values":[]},{"schema":{"name":"contentType","displayName":"contentType","helpMessage":"contentType","type":"java.lang.String","required":true,"order":-1,"confidential":false,"defaultValues":["application/json"]},"overridable":false,"values":["application/json"]},{"schema":{"name":"resolveUsernameScriptFileName","displayName":"resolveUsernameScriptFileName","helpMessage":"resolveUsernameScriptFileName","type":"java.lang.String","required":false,"order":15,"confidential":false,"defaultValues":[]},"overridable":false,"values":[]},{"schema":{"name":"createScriptFileName","displayName":"createScriptFileName","helpMessage":"createScriptFileName","type":"java.lang.String","required":false,"order":10,"confidential":false,"defaultValues":[]},"overridable":false,"values":["${syncope.conf.dir}/rest/CreateScript.groovy"]},{"schema":{"name":"username","displayName":"username","helpMessage":"username","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":[]},"overridable":false,"values":[]},{"schema":{"name":"updateScript","displayName":"updateScript","helpMessage":"updateScript","type":"java.lang.String","required":false,"order":4,"confidential":false,"defaultValues":[""]},"overridable":false,"values":[]},{"schema":{"name":"searchScript","displayName":"searchScript","helpMessage":"searchScript","type":"java.lang.String","required":false,"order":6,"confidential":false,"defaultValues":[""]},"overridable":false,"values":[]},{"schema":{"name":"clearTextPasswordToScript","displayName":"clearTextPasswordToScript","helpMessage":"clearTextPasswordToScript","type":"boolean","required":false,"order":1,"confidential":false,"defaultValues":[true]},"overridable":false,"values":[true]},{"schema":{"name":"syncScript","displayName":"syncScript","helpMessage":"syncScript","type":"java.lang.String","required":false,"order":7,"confidential":false,"defaultValues":[""]},"overridable":false,"values":[]},{"schema":{"name":"deleteScriptFileName","displayName":"deleteScriptFileName","helpMessage":"deleteScriptFileName","type":"java.lang.String","required":false,"order":12,"confidential":false,"defaultValues":[]},"overridable":false,"values":["${syncope.conf.dir}/rest/DeleteScript.groovy"]},{"schema":{"name":"resolveUsernameScript","displayName":"resolveUsernameScript","helpMessage":"resolveUsernameScript","type":"java.lang.String","required":false,"order":6,"confidential":false,"defaultValues":[""]},"overridable":false,"values":[]},{"schema":{"name":"searchScriptFileName","displayName":"searchScriptFileName","helpMessage":"searchScriptFileName","type":"java.lang.String","required":false,"order":13,"confidential":false,"defaultValues":[]},"overridable":false,"values":["${syncope.conf.dir}/rest/SearchScript.groovy"]},{"schema":{"name":"syncScriptFileName","displayName":"syncScriptFileName","helpMessage":"syncScriptFileName","type":"java.lang.String","required":false,"order":16,"confidential":false,"defaultValues":[]},"overridable":false,"values":["${syncope.conf.dir}/rest/SyncScript.groovy"]},{"schema":{"name":"schemaScriptFileName","displayName":"schemaScriptFileName","helpMessage":"schemaScriptFileName","type":"java.lang.String","required":false,"order":17,"confidential":false,"defaultValues":[]},"overridable":false,"values":["${syncope.conf.dir}/rest/SchemaScript.groovy"]},{"schema":{"name":"password","displayName":"password","helpMessage":"password","type":"org.identityconnectors.common.security.GuardedString","required":false,"order":1,"confidential":true,"defaultValues":[]},"overridable":false,"values":[]},{"schema":{"name":"updateScriptFileName","displayName":"updateScriptFileName","helpMessage":"updateScriptFileName","type":"java.lang.String","required":false,"order":11,"confidential":false,"defaultValues":[]},"overridable":false,"values":["${syncope.conf.dir}/rest/UpdateScript.groovy"]},{"schema":{"name":"testScriptFileName","displayName":"testScriptFileName","helpMessage":"testScriptFileName","type":"java.lang.String","required":false,"order":18,"confidential":false,"defaultValues":[]},"overridable":false,"values":["${syncope.conf.dir}/rest/TestScript.groovy"]},{"schema":{"name":"accept","displayName":"accept","helpMessage":"accept","type":"java.lang.String","required":true,"order":-2,"confidential":false,"defaultValues":["application/json"]},"overridable":false,"values":["application/json"]},{"schema":{"name":"baseAddress","displayName":"baseAddress","helpMessage":"baseAddress","type":"java.lang.String","required":true,"order":-3,"confidential":false,"defaultValues":[]},"overridable":false,"values":["http://localhost:${cargo.servlet.port}/syncope-fit-build-tools/cxf/rest"]},{"schema":{"name":"authenticateScriptFileName","displayName":"authenticateScriptFileName","helpMessage":"authenticateScriptFileName","type":"java.lang.String","required":false,"order":14,"confidential":false,"defaultValues":[]},"overridable":false,"values":["${syncope.conf.dir}/rest/AuthenticateScript.groovy"]},{"schema":{"name":"deleteScript","displayName":"deleteScript","helpMessage":"deleteScript","type":"java.lang.String","required":false,"order":5,"confidential":false,"defaultValues":[""]},"overridable":false,"values":[]},{"schema":{"name":"schemaScript","displayName":"schemaScript","helpMessage":"schemaScript","type":"java.lang.String","required":false,"order":8,"confidential":false,"defaultValues":[""]},"overridable":false,"values":[]},{"schema":{"name":"createScript","displayName":"createScript","helpMessage":"createScript","type":"java.lang.String","required":false,"order":3,"confidential":false,"defaultValues":[""]},"overridable":false,"values":[]},{"schema":{"name":"scriptingLanguage","displayName":"scriptingLanguage","helpMessage":"scriptingLanguage","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":["GROOVY"]},"overridable":false,"values":["GROOVY"]},{"schema":{"name":"testScript","displayName":"testScript","helpMessage":"testScript","type":"java.lang.String","required":false,"order":9,"confidential":false,"defaultValues":[""]},"overridable":false,"values":[]},{"schema":{"name":"reloadScriptOnExecution","displayName":"reloadScriptOnExecution","helpMessage":"reloadScriptOnExecution","type":"boolean","required":false,"order":2,"confidential":false,"defaultValues":[false]},"overridable":false,"values":[false]}]" capabilities='["AUTHENTICATE","CREATE","UPDATE","DELETE","SEARCH","SYNC"]'/> + + @@ -619,52 +631,58 @@ under the License. body="org.apache.syncope.core.provisioning.java.propagation.LDAPMembershipPropagationActions"/> + + @@ -692,7 +710,8 @@ under the License. - + - { return Neo4jPushPolicy.NODE + ":" + Neo4jPolicy.NODE; } - case Neo4jPullPolicy.NODE -> { - return Neo4jPullPolicy.NODE + ":" + Neo4jPolicy.NODE; + case Neo4jInboundPolicy.NODE -> { + return Neo4jInboundPolicy.NODE + ":" + Neo4jPolicy.NODE; } case Neo4jTicketExpirationPolicy.NODE -> { return Neo4jTicketExpirationPolicy.NODE + ":" + Neo4jPolicy.NODE; @@ -119,7 +121,12 @@ protected static String nodelabels(final String primaryLabel) { return Neo4jPushTask.NODE + ":" + Neo4jProvisioningTask.NODE + ":" + Neo4jSchedTask.NODE; } case Neo4jPullTask.NODE -> { - return Neo4jPullTask.NODE + ":" + Neo4jProvisioningTask.NODE + ":" + Neo4jSchedTask.NODE; + return Neo4jPullTask.NODE + ":" + Neo4jInboundTask.NODE + ":" + + Neo4jProvisioningTask.NODE + ":" + Neo4jSchedTask.NODE; + } + case Neo4jLiveSyncTask.NODE -> { + return Neo4jLiveSyncTask.NODE + ":" + Neo4jInboundTask.NODE + ":" + + Neo4jProvisioningTask.NODE + ":" + Neo4jSchedTask.NODE; } case Neo4jMacroTask.NODE -> { return Neo4jMacroTask.NODE + ":" + Neo4jSchedTask.NODE; diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/XMLContentExporter.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/XMLContentExporter.java index 015e7c64e73..1234c07fa9a 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/XMLContentExporter.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/content/XMLContentExporter.java @@ -42,6 +42,7 @@ import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRealm; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jSchema; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jInboundTask; import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jMacroTask; import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jMacroTaskCommandRelationship; import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jProvisioningTask; @@ -60,7 +61,7 @@ public class XMLContentExporter extends AbstractXMLContentExporter { protected static final Set LABELS_TO_BE_EXCLUDED = Set.of( - Neo4jSchema.NODE, Neo4jPolicy.NODE, Neo4jProvisioningTask.NODE, + Neo4jSchema.NODE, Neo4jPolicy.NODE, Neo4jProvisioningTask.NODE, Neo4jInboundTask.NODE, Neo4jJobStatus.NODE, Neo4jAuditEvent.NODE); protected static final Comparator REALM_COMPARATOR = @@ -203,6 +204,7 @@ public void export( StringBuilder query = new StringBuilder("MATCH (n:" + entity.getPrimaryLabel() + ")-[r]-() "); if (Neo4jSchedTask.NODE.equals(entity.getPrimaryLabel())) { query.append("WHERE NOT n:").append(Neo4jMacroTask.NODE). + append(" AND NOT n:").append(Neo4jInboundTask.NODE). append(" AND NOT n:").append(Neo4jProvisioningTask.NODE).append(' '); } query.append("RETURN n, collect(r) AS rels ORDER BY n.id"); diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jPolicyDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jPolicyDAO.java index ef88c1374b1..93f913a1de3 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jPolicyDAO.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jPolicyDAO.java @@ -34,10 +34,10 @@ import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy; import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy; import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.InboundPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy; import org.apache.syncope.core.persistence.api.entity.policy.Policy; import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy; -import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; @@ -46,11 +46,11 @@ import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAccountPolicy; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAttrReleasePolicy; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAuthPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jInboundCorrelationRuleEntity; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jInboundPolicy; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPasswordPolicy; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPolicy; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPropagationPolicy; -import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPullCorrelationRuleEntity; -import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPullPolicy; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPushCorrelationRuleEntity; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPushPolicy; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jTicketExpirationPolicy; @@ -71,8 +71,8 @@ protected static Class getNodeReferenc ? Neo4jPasswordPolicy.class : PropagationPolicy.class.isAssignableFrom(reference) ? Neo4jPropagationPolicy.class - : PullPolicy.class.isAssignableFrom(reference) - ? Neo4jPullPolicy.class + : InboundPolicy.class.isAssignableFrom(reference) + ? Neo4jInboundPolicy.class : PushPolicy.class.isAssignableFrom(reference) ? Neo4jPushPolicy.class : AuthPolicy.class.isAssignableFrom(reference) @@ -179,12 +179,12 @@ public List findByPasswordRule(final Implementation passwordRule } @Override - public List findByPullCorrelationRule(final Implementation correlationRule) { + public List findByInboundCorrelationRule(final Implementation correlationRule) { return findByRelationship( - Neo4jPullPolicy.NODE, + Neo4jInboundPolicy.NODE, Neo4jImplementation.NODE, correlationRule.getKey(), - Neo4jPullPolicy.class, + Neo4jInboundPolicy.class, null); } @@ -240,7 +240,7 @@ public

    P save(final P policy) { if (policy instanceof AccountPolicy || policy instanceof PasswordPolicy || policy instanceof PropagationPolicy - || policy instanceof PullPolicy + || policy instanceof InboundPolicy || policy instanceof PushPolicy) { resourceDAO.findByPolicy(policy). @@ -260,12 +260,11 @@ public void delete(final Policy policy) { resourceDAO.findByPolicy(policy).forEach(resource -> resource.setPasswordPolicy(null)); } else if (policy instanceof PropagationPolicy) { resourceDAO.findByPolicy(policy).forEach(resource -> resource.setPropagationPolicy(null)); - } else if (policy instanceof PullPolicy) { - resourceDAO.findByPolicy(policy).forEach(resource -> resource.setPullPolicy(null)); + } else if (policy instanceof InboundPolicy) { + resourceDAO.findByPolicy(policy).forEach(resource -> resource.setInboundPolicy(null)); - cascadeDelete( - Neo4jPullCorrelationRuleEntity.NODE, - Neo4jPullPolicy.NODE, + cascadeDelete(Neo4jInboundCorrelationRuleEntity.NODE, + Neo4jInboundPolicy.NODE, policy.getKey()); } else if (policy instanceof PushPolicy) { resourceDAO.findByPolicy(policy).forEach(resource -> resource.setPushPolicy(null)); diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskDAO.java index b48a41b9cf3..aa996dc1e40 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskDAO.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskDAO.java @@ -159,7 +159,7 @@ public > Optional findById(final TaskType type, final Strin public Optional findByName(final TaskType type, final String name) { TaskUtils taskUtils = taskUtilsFactory.getInstance(type); return neo4jClient.query( - "MATCH (n:" + taskUtils.getTaskTable() + ") WHERE n.name = $name RETURN n.id"). + "MATCH (n:" + taskUtils.getTaskStorage() + ") WHERE n.name = $name RETURN n.id"). bindAll(Map.of("name", name)).fetch().one(). flatMap(toOptional("n.id", (Class>) taskUtils.getTaskEntity(), null)); } @@ -199,9 +199,9 @@ public List findByReconFilterBuilder(final Implementation reconFilterB } @Override - public List findByPullActions(final Implementation pullActions) { + public List findByInboundActions(final Implementation inboundActions) { return findByRelationship( - Neo4jPullTask.NODE, Neo4jImplementation.NODE, pullActions.getKey(), Neo4jPullTask.class, null); + Neo4jPullTask.NODE, Neo4jImplementation.NODE, inboundActions.getKey(), Neo4jPullTask.class, null); } @Override @@ -225,7 +225,7 @@ public List findByCommand(final Implementation command) { @SuppressWarnings("unchecked") public > List findToExec(final TaskType type) { TaskUtils taskUtils = taskUtilsFactory.getInstance(type); - StringBuilder query = new StringBuilder("MATCH (n:" + taskUtils.getTaskTable() + ") WHERE "); + StringBuilder query = new StringBuilder("MATCH (n:" + taskUtils.getTaskStorage() + ") WHERE "); if (type == TaskType.NOTIFICATION) { query.append("n.executed = false "); @@ -261,7 +261,10 @@ protected StringBuilder query( final Map parameters) { if (resource != null - && type != TaskType.PROPAGATION && type != TaskType.PUSH && type != TaskType.PULL) { + && type != TaskType.PROPAGATION + && type != TaskType.LIVE_SYNC + && type != TaskType.PUSH + && type != TaskType.PULL) { throw new IllegalArgumentException(type + " is not related to " + ExternalResource.class.getSimpleName()); } @@ -292,7 +295,7 @@ protected StringBuilder query( } relationships.add(Pair.of( - (optionalMatch ? "OPTIONAL " : "") + "MATCH (n)-[]-(p:" + taskUtils.getTaskExecTable() + ")", null)); + (optionalMatch ? "OPTIONAL " : "") + "MATCH (n)-[]-(p:" + taskUtils.getTaskExecStorage() + ")", null)); if (resource != null) { relationships.add(Pair.of("MATCH (n)-[]-(e:" + Neo4jExternalResource.NODE + " {id: $eid})", null)); @@ -324,7 +327,7 @@ protected StringBuilder query( relationships.add(Pair.of("MATCH (n)-[]-(q:" + Neo4jRealm.NODE + ")", "(" + realmCond + ")")); } - StringBuilder query = new StringBuilder("MATCH (n:").append(taskUtils.getTaskTable()).append(")"); + StringBuilder query = new StringBuilder("MATCH (n:").append(taskUtils.getTaskStorage()).append(")"); if (type == TaskType.SCHEDULED) { query.append( @@ -484,12 +487,11 @@ public > T save(final T task) { } t.getActions().stream().filter(act -> !pullTask.getActions().contains(act)). - forEach(impl -> deleteRelationship( - Neo4jPullTask.NODE, + forEach(impl -> deleteRelationship(Neo4jPullTask.NODE, Neo4jImplementation.NODE, pullTask.getKey(), impl.getKey(), - Neo4jPullTask.PULL_TASK_PULL_ACTIONS_REL)); + Neo4jPullTask.PULL_TASK_INBOUND_ACTIONS_REL)); }); case Neo4jPushTask pushTask -> { diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskExecDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskExecDAO.java index 0e3a60853d0..35609710a87 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskExecDAO.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jTaskExecDAO.java @@ -100,7 +100,7 @@ public Optional> findById(final String key) { protected > List> findRecent(final TaskType type, final int max) { TaskUtils taskUtils = taskUtilsFactory.getInstance(type); return toList(neo4jClient.query( - "MATCH (n:" + taskUtils.getTaskExecTable() + ")-[]-(p:" + taskUtils.getTaskTable() + " {id: $id}) " + "MATCH (n:" + taskUtils.getTaskExecStorage() + ")-[]-(p:" + taskUtils.getTaskStorage() + " {id: $id}) " + "WHERE n.endDate IS NOT NULL " + "RETURN n.id ORDER BY n.endDate DESC LIMIT " + max).fetch().all(), "n.id", @@ -126,7 +126,7 @@ public List> findRecent(final int max) { protected Optional> findLatest(final TaskType type, final Task task, final String field) { TaskUtils taskUtils = taskUtilsFactory.getInstance(type); return neo4jClient.query( - "MATCH (n:" + taskUtils.getTaskExecTable() + ")-[]-(p:" + taskUtils.getTaskTable() + " {id: $id}) " + "MATCH (n:" + taskUtils.getTaskExecStorage() + ")-[]-(p:" + taskUtils.getTaskStorage() + " {id: $id}) " + "RETURN n.id ORDER BY n." + field + " DESC LIMIT 1"). bindAll(Map.of("id", task.getKey())).fetch().one(). flatMap(toOptional("n.id", (Class>) taskUtils.getTaskExecEntity(), null)); @@ -158,9 +158,9 @@ protected StringBuilder query( parameters.put("id", task.getKey()); StringBuilder query = new StringBuilder( - "MATCH (n:").append(taskUtils.getTaskExecTable()).append(")-"). + "MATCH (n:").append(taskUtils.getTaskExecStorage()).append(")-"). append("[:").append(Neo4jTaskDAO.execRelationship(taskUtils.getType())).append("]-"). - append("(p:").append(taskUtils.getTaskTable()).append(" {id: $id}) "). + append("(p:").append(taskUtils.getTaskStorage()).append(" {id: $id}) "). append("WHERE 1=1 "); if (before != null) { diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ExternalResourceRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ExternalResourceRepoExtImpl.java index 2d6c92eaa8f..deeea6cd606 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ExternalResourceRepoExtImpl.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/ExternalResourceRepoExtImpl.java @@ -34,10 +34,10 @@ import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.Implementation; import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.InboundPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy; import org.apache.syncope.core.persistence.api.entity.policy.Policy; import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy; -import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; import org.apache.syncope.core.persistence.neo4j.dao.AbstractDAO; import org.apache.syncope.core.persistence.neo4j.entity.EntityCacheKey; @@ -45,10 +45,10 @@ import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jImplementation; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAccountPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jInboundPolicy; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPasswordPolicy; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPolicy; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPropagationPolicy; -import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPullPolicy; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPushPolicy; import org.apache.syncope.core.persistence.neo4j.entity.user.Neo4jLinkedAccount; import org.apache.syncope.core.persistence.neo4j.spring.NodeValidator; @@ -171,9 +171,9 @@ public List findByPolicy(final Policy policy) { } else if (policy instanceof PushPolicy) { relationship = Neo4jExternalResource.RESOURCE_PUSH_POLICY_REL; label = Neo4jPushPolicy.NODE + ":" + Neo4jPolicy.NODE; - } else if (policy instanceof PullPolicy) { - relationship = Neo4jExternalResource.RESOURCE_PULL_POLICY_REL; - label = Neo4jPullPolicy.NODE + ":" + Neo4jPolicy.NODE; + } else if (policy instanceof InboundPolicy) { + relationship = Neo4jExternalResource.RESOURCE_INBOUND_POLICY_REL; + label = Neo4jInboundPolicy.NODE + ":" + Neo4jPolicy.NODE; } return findByRelationship( @@ -233,13 +233,12 @@ public ExternalResource save(final ExternalResource resource) { before.getPropagationPolicy().getKey(), Neo4jExternalResource.RESOURCE_PROPAGATION_POLICY_REL); } - if (before.getPullPolicy() != null && resource.getPullPolicy() == null) { - deleteRelationship( - Neo4jExternalResource.NODE, - Neo4jPullPolicy.NODE, + if (before.getInboundPolicy() != null && resource.getInboundPolicy() == null) { + deleteRelationship(Neo4jExternalResource.NODE, + Neo4jInboundPolicy.NODE, resource.getKey(), - before.getPullPolicy().getKey(), - Neo4jExternalResource.RESOURCE_PULL_POLICY_REL); + before.getInboundPolicy().getKey(), + Neo4jExternalResource.RESOURCE_INBOUND_POLICY_REL); } if (before.getPushPolicy() != null && resource.getPushPolicy() == null) { deleteRelationship( diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jConnInstance.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jConnInstance.java index 94971e4d904..a4f7ebe7ee3 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jConnInstance.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jConnInstance.java @@ -21,7 +21,6 @@ import com.fasterxml.jackson.core.type.TypeReference; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; -import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Optional; @@ -48,10 +47,14 @@ public class Neo4jConnInstance extends AbstractGeneratedKeyNode implements ConnI public static final String NODE = "ConnInstance"; - protected static final TypeReference> TYPEREF = + protected static final TypeReference> CONNECTOR_CAPABILITY_TYPEREF = new TypeReference>() { }; + protected static final TypeReference> CONN_CONF_PROPS_TYPEREF = + new TypeReference>() { + }; + private static final int DEFAULT_TIMEOUT = 10; /** @@ -61,25 +64,19 @@ public class Neo4jConnInstance extends AbstractGeneratedKeyNode implements ConnI private String location; /** - * Connector bundle class name. - * Within a given location, the triple - * (ConnectorBundle-Name, ConnectorBundle-Version, ConnectorBundle-Version) must be unique. + * Connector class name. */ @NotNull private String connectorName; /** - * Qualified name for the connector bundle. - * Within a given location, the triple - * (ConnectorBundle-Name, ConnectorBundle-Version, ConnectorBundle-Version) must be unique. + * Connector bundle name. */ @NotNull private String bundleName; /** - * Version of the bundle. - * Within a given location, the triple - * (ConnectorBundle-Name, ConnectorBundle-Version, ConnectorBundle-Version) must be unique. + * Connector bundle version. */ @NotNull private String version; @@ -100,8 +97,9 @@ public class Neo4jConnInstance extends AbstractGeneratedKeyNode implements ConnI private String displayName; /** - * Connector request timeout. It is not applied in case of sync, full reconciliation and search. - * DEFAULT_TIMEOUT is the default value to be used in case of unspecified timeout. + * Connection request timeout. + * It is not applied in case of sync, full reconciliation and search. + * DEFAULT_TIMEOUT if null. */ private Integer connRequestTimeout = DEFAULT_TIMEOUT; @@ -168,18 +166,15 @@ public void setVersion(final String version) { } @Override - public Set getConf() { - Set configuration = new HashSet<>(); - if (!StringUtils.isBlank(jsonConf)) { - configuration.addAll(List.of(POJOHelper.deserialize(jsonConf, ConnConfProperty[].class))); - } - - return configuration; + public List getConf() { + return StringUtils.isNotBlank(jsonConf) + ? POJOHelper.deserialize(jsonConf, CONN_CONF_PROPS_TYPEREF) + : new ArrayList<>(); } @Override - public void setConf(final Collection conf) { - jsonConf = POJOHelper.serialize(new HashSet<>(conf)); + public void setConf(final List conf) { + jsonConf = POJOHelper.serialize(conf); } @Override @@ -210,9 +205,6 @@ public Set getCapabilities() { @Override public Integer getConnRequestTimeout() { - // DEFAULT_TIMEOUT will be returned in case of null timeout: - // * instances created by the content loader - // * or with a timeout nullified explicitely return Optional.ofNullable(connRequestTimeout).orElse(DEFAULT_TIMEOUT); } @@ -236,7 +228,7 @@ protected void json2list(final boolean clearFirst) { getCapabilities().clear(); } if (capabilities != null) { - getCapabilities().addAll(POJOHelper.deserialize(capabilities, TYPEREF)); + getCapabilities().addAll(POJOHelper.deserialize(capabilities, CONNECTOR_CAPABILITY_TYPEREF)); } } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jEntityFactory.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jEntityFactory.java index 10b0a5a71b4..9b64cb8b053 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jEntityFactory.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jEntityFactory.java @@ -80,15 +80,16 @@ import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy; import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy; import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.InboundCorrelationRuleEntity; +import org.apache.syncope.core.persistence.api.entity.policy.InboundPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy; -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.policy.PushCorrelationRuleEntity; import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy; import org.apache.syncope.core.persistence.api.entity.task.AnyTemplatePullTask; import org.apache.syncope.core.persistence.api.entity.task.FormPropertyDef; +import org.apache.syncope.core.persistence.api.entity.task.LiveSyncTask; import org.apache.syncope.core.persistence.api.entity.task.MacroTask; import org.apache.syncope.core.persistence.api.entity.task.MacroTaskCommand; import org.apache.syncope.core.persistence.api.entity.task.NotificationTask; @@ -137,15 +138,16 @@ import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAccountPolicy; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAttrReleasePolicy; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAuthPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jInboundCorrelationRuleEntity; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jInboundPolicy; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPasswordPolicy; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPropagationPolicy; -import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPullCorrelationRuleEntity; -import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPullPolicy; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPushCorrelationRuleEntity; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPushPolicy; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jTicketExpirationPolicy; import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jAnyTemplatePullTask; import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jFormPropertyDef; +import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jLiveSyncTask; import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jMacroTask; import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jMacroTaskCommand; import org.apache.syncope.core.persistence.neo4j.entity.task.Neo4jNotificationTask; @@ -190,10 +192,10 @@ public E newEntity(final Class reference) { result = (E) new Neo4jPropagationPolicy(); } else if (reference.equals(PushPolicy.class)) { result = (E) new Neo4jPushPolicy(); - } else if (reference.equals(PullPolicy.class)) { - result = (E) new Neo4jPullPolicy(); - } else if (reference.equals(PullCorrelationRuleEntity.class)) { - result = (E) new Neo4jPullCorrelationRuleEntity(); + } else if (reference.equals(InboundPolicy.class)) { + result = (E) new Neo4jInboundPolicy(); + } else if (reference.equals(InboundCorrelationRuleEntity.class)) { + result = (E) new Neo4jInboundCorrelationRuleEntity(); } else if (reference.equals(PushCorrelationRuleEntity.class)) { result = (E) new Neo4jPushCorrelationRuleEntity(); } else if (reference.equals(AnyTypeClass.class)) { @@ -276,6 +278,8 @@ public E newEntity(final Class reference) { result = (E) new Neo4jPropagationTask(); } else if (reference.equals(PushTask.class)) { result = (E) new Neo4jPushTask(); + } else if (reference.equals(LiveSyncTask.class)) { + result = (E) new Neo4jLiveSyncTask(); } else if (reference.equals(PullTask.class)) { result = (E) new Neo4jPullTask(); } else if (reference.equals(MacroTask.class)) { diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jExternalResource.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jExternalResource.java index 433a83b6288..688e74ba9c4 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jExternalResource.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/Neo4jExternalResource.java @@ -21,7 +21,6 @@ import com.fasterxml.jackson.core.type.TypeReference; import jakarta.validation.constraints.NotNull; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; @@ -38,15 +37,15 @@ import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.Implementation; import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.InboundPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy; -import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; import org.apache.syncope.core.persistence.common.validation.ExternalResourceCheck; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jAccountPolicy; +import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jInboundPolicy; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPasswordPolicy; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPropagationPolicy; -import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPullPolicy; import org.apache.syncope.core.persistence.neo4j.entity.policy.Neo4jPushPolicy; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; import org.springframework.data.annotation.Transient; @@ -70,7 +69,7 @@ public class Neo4jExternalResource extends AbstractProvidedKeyNode implements Ex public static final String RESOURCE_PROPAGATION_POLICY_REL = "RESOURCE_PROPAGATION_POLICY"; - public static final String RESOURCE_PULL_POLICY_REL = "RESOURCE_PULL_POLICY"; + public static final String RESOURCE_INBOUND_POLICY_REL = "RESOURCE_INBOUND_POLICY"; public static final String RESOURCE_PUSH_POLICY_REL = "RESOURCE_PUSH_POLICY"; @@ -78,7 +77,11 @@ public class Neo4jExternalResource extends AbstractProvidedKeyNode implements Ex public static final String RESOURCE_PROPAGATION_ACTIONS_REL = "RESOURCE_PROPAGATION_ACTIONS"; - protected static final TypeReference> CAPABILITY_TYPEREF = + protected static final TypeReference> CONN_CONF_PROPS_TYPEREF = + new TypeReference>() { + }; + + protected static final TypeReference> CONNECTOR_CAPABILITY_TYPEREF = new TypeReference>() { }; @@ -114,14 +117,8 @@ public class Neo4jExternalResource extends AbstractProvidedKeyNode implements Ex */ private String jsonConf; - @NotNull - private Boolean overrideCapabilities = false; - private String capabilitiesOverride; - @Transient - private final Set capabilitiesOverrideSet = new HashSet<>(); - private String provisions; @Transient @@ -144,8 +141,8 @@ public class Neo4jExternalResource extends AbstractProvidedKeyNode implements Ex @Relationship(type = RESOURCE_PROPAGATION_POLICY_REL, direction = Relationship.Direction.OUTGOING) private Neo4jPropagationPolicy propagationPolicy; - @Relationship(type = RESOURCE_PULL_POLICY_REL, direction = Relationship.Direction.OUTGOING) - private Neo4jPullPolicy pullPolicy; + @Relationship(type = RESOURCE_INBOUND_POLICY_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jInboundPolicy inboundPolicy; @Relationship(type = RESOURCE_PUSH_POLICY_REL, direction = Relationship.Direction.OUTGOING) private Neo4jPushPolicy pushPolicy; @@ -172,14 +169,12 @@ public void setEnforceMandatoryCondition(final boolean enforceMandatoryCondition @Override public Optional getProvisionByAnyType(final String anyType) { - return getProvisions().stream(). - filter(provision -> provision.getAnyType().equals(anyType)).findFirst(); + return getProvisions().stream().filter(provision -> provision.getAnyType().equals(anyType)).findFirst(); } @Override public Optional getProvisionByObjectClass(final String objectClass) { - return getProvisions().stream(). - filter(provision -> provision.getObjectClass().equals(objectClass)).findFirst(); + return getProvisions().stream().filter(provision -> provision.getObjectClass().equals(objectClass)).findFirst(); } @Override @@ -248,36 +243,6 @@ public void setProvisioningTraceLevel(final TraceLevel provisioningTraceLevel) { this.provisioningTraceLevel = provisioningTraceLevel; } - @Override - public Set getConfOverride() { - Set confOverride = new HashSet<>(); - if (!StringUtils.isBlank(jsonConf)) { - confOverride.addAll(List.of(POJOHelper.deserialize(jsonConf, ConnConfProperty[].class))); - } - - return confOverride; - } - - @Override - public void setConfOverride(final Set confOverride) { - jsonConf = POJOHelper.serialize(new HashSet<>(confOverride)); - } - - @Override - public boolean isOverrideCapabilities() { - return overrideCapabilities; - } - - @Override - public void setOverrideCapabilities(final boolean overrideCapabilities) { - this.overrideCapabilities = overrideCapabilities; - } - - @Override - public Set getCapabilitiesOverride() { - return capabilitiesOverrideSet; - } - @Override public ConnInstance getConnector() { return connector; @@ -323,14 +288,14 @@ public void setPropagationPolicy(final PropagationPolicy propagationPolicy) { } @Override - public PullPolicy getPullPolicy() { - return pullPolicy; + public InboundPolicy getInboundPolicy() { + return inboundPolicy; } @Override - public void setPullPolicy(final PullPolicy pullPolicy) { - checkType(pullPolicy, Neo4jPullPolicy.class); - this.pullPolicy = (Neo4jPullPolicy) pullPolicy; + public void setInboundPolicy(final InboundPolicy inboundPolicy) { + checkType(inboundPolicy, Neo4jInboundPolicy.class); + this.inboundPolicy = (Neo4jInboundPolicy) inboundPolicy; } @Override @@ -356,6 +321,34 @@ public void setProvisionSorter(final Implementation provisionSorter) { this.provisionSorter = (Neo4jImplementation) provisionSorter; } + @Override + public Optional> getConfOverride() { + return StringUtils.isBlank(jsonConf) + ? Optional.empty() + : Optional.of(POJOHelper.deserialize(jsonConf, CONN_CONF_PROPS_TYPEREF)); + } + + @Override + public void setConfOverride(final Optional> confOverride) { + confOverride.ifPresentOrElse( + conf -> jsonConf = POJOHelper.serialize(conf), + () -> jsonConf = null); + } + + @Override + public Optional> getCapabilitiesOverride() { + return StringUtils.isBlank(capabilitiesOverride) + ? Optional.empty() + : Optional.of(POJOHelper.deserialize(capabilitiesOverride, CONNECTOR_CAPABILITY_TYPEREF)); + } + + @Override + public void setCapabilitiesOverride(final Optional> capabilitiesOverride) { + capabilitiesOverride.ifPresentOrElse( + override -> this.capabilitiesOverride = POJOHelper.serialize(override), + () -> this.capabilitiesOverride = null); + } + @Override public boolean add(final Implementation propagationAction) { checkType(propagationAction, Neo4jImplementation.class); @@ -371,12 +364,8 @@ public List getPropagationActions() { protected void json2list(final boolean clearFirst) { if (clearFirst) { - getCapabilitiesOverride().clear(); getProvisions().clear(); } - if (capabilitiesOverride != null) { - getCapabilitiesOverride().addAll(POJOHelper.deserialize(capabilitiesOverride, CAPABILITY_TYPEREF)); - } if (provisions != null) { getProvisions().addAll(POJOHelper.deserialize(provisions, PROVISION_TYPEREF)); } @@ -393,7 +382,6 @@ public void postSave() { } public void list2json() { - capabilitiesOverride = POJOHelper.serialize(getCapabilitiesOverride()); provisions = POJOHelper.serialize(getProvisions()); } } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPullCorrelationRuleEntity.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jInboundCorrelationRuleEntity.java similarity index 61% rename from core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPullCorrelationRuleEntity.java rename to core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jInboundCorrelationRuleEntity.java index 2e0130ac01a..bd43d92c14c 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPullCorrelationRuleEntity.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jInboundCorrelationRuleEntity.java @@ -19,34 +19,36 @@ package org.apache.syncope.core.persistence.neo4j.entity.policy; import org.apache.syncope.common.lib.types.IdMImplementationType; -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.policy.InboundCorrelationRuleEntity; +import org.apache.syncope.core.persistence.api.entity.policy.InboundPolicy; import org.springframework.data.neo4j.core.schema.Node; import org.springframework.data.neo4j.core.schema.Relationship; -@Node(Neo4jPullCorrelationRuleEntity.NODE) -public class Neo4jPullCorrelationRuleEntity extends AbstractCorrelationRuleEntity implements PullCorrelationRuleEntity { +@Node(Neo4jInboundCorrelationRuleEntity.NODE) +public class Neo4jInboundCorrelationRuleEntity + extends AbstractCorrelationRuleEntity + implements InboundCorrelationRuleEntity { private static final long serialVersionUID = 4276417265524083919L; - public static final String NODE = "PullCorrelationRuleEntity"; + public static final String NODE = "InboundCorrelationRuleEntity"; @Relationship(direction = Relationship.Direction.OUTGOING) - private Neo4jPullPolicy pullPolicy; + private Neo4jInboundPolicy inboundPolicy; @Override protected String getImplementationType() { - return IdMImplementationType.PULL_CORRELATION_RULE; + return IdMImplementationType.INBOUND_CORRELATION_RULE; } @Override - public PullPolicy getPullPolicy() { - return pullPolicy; + public InboundPolicy getInboundPolicy() { + return inboundPolicy; } @Override - public void setPullPolicy(final PullPolicy pullPolicy) { - checkType(pullPolicy, Neo4jPullPolicy.class); - this.pullPolicy = (Neo4jPullPolicy) pullPolicy; + public void setInboundPolicy(final InboundPolicy inboundPolicy) { + checkType(inboundPolicy, Neo4jInboundPolicy.class); + this.inboundPolicy = (Neo4jInboundPolicy) inboundPolicy; } } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPullPolicy.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jInboundPolicy.java similarity index 67% rename from core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPullPolicy.java rename to core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jInboundPolicy.java index efdd0580c59..c1e74a70a3f 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jPullPolicy.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/policy/Neo4jInboundPolicy.java @@ -23,23 +23,23 @@ import java.util.List; import java.util.Optional; import org.apache.syncope.common.lib.types.ConflictResolutionAction; -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.policy.InboundCorrelationRuleEntity; +import org.apache.syncope.core.persistence.api.entity.policy.InboundPolicy; import org.springframework.data.neo4j.core.schema.Node; import org.springframework.data.neo4j.core.schema.Relationship; -@Node(Neo4jPullPolicy.NODE) -public class Neo4jPullPolicy extends Neo4jPolicy implements PullPolicy { +@Node(Neo4jInboundPolicy.NODE) +public class Neo4jInboundPolicy extends Neo4jPolicy implements InboundPolicy { private static final long serialVersionUID = -6090413855809521279L; - public static final String NODE = "PullPolicy"; + public static final String NODE = "InboundPolicy"; @NotNull private ConflictResolutionAction conflictResolutionAction; - @Relationship(type = "PULL_POLICY", direction = Relationship.Direction.INCOMING) - private List correlationRules = new ArrayList<>(); + @Relationship(type = "INBOUND_POLICY", direction = Relationship.Direction.INCOMING) + private List correlationRules = new ArrayList<>(); @Override public ConflictResolutionAction getConflictResolutionAction() { @@ -52,19 +52,19 @@ public void setConflictResolutionAction(final ConflictResolutionAction conflictR } @Override - public boolean add(final PullCorrelationRuleEntity filter) { - checkType(filter, Neo4jPullCorrelationRuleEntity.class); - return this.correlationRules.add((Neo4jPullCorrelationRuleEntity) filter); + public boolean add(final InboundCorrelationRuleEntity filter) { + checkType(filter, Neo4jInboundCorrelationRuleEntity.class); + return this.correlationRules.add((Neo4jInboundCorrelationRuleEntity) filter); } @Override - public Optional getCorrelationRule(final String anyType) { + public Optional getCorrelationRule(final String anyType) { return correlationRules.stream(). filter(rule -> anyType != null && anyType.equals(rule.getAnyType().getKey())).findFirst(); } @Override - public List getCorrelationRules() { + public List getCorrelationRules() { return correlationRules; } } diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jAnyTemplateLiveSyncTask.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jAnyTemplateLiveSyncTask.java new file mode 100644 index 00000000000..a4f0b039896 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jAnyTemplateLiveSyncTask.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.neo4j.entity.task; + +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.task.AnyTemplateLiveSyncTask; +import org.apache.syncope.core.persistence.api.entity.task.LiveSyncTask; +import org.apache.syncope.core.persistence.neo4j.entity.AbstractAnyTemplate; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jAnyTemplateLiveSyncTask.NODE) +public class Neo4jAnyTemplateLiveSyncTask extends AbstractAnyTemplate implements AnyTemplateLiveSyncTask { + + private static final long serialVersionUID = 3517381731849788407L; + + public static final String NODE = "AnyTemplateLiveSyncTask"; + + @NotNull + @Relationship(type = Neo4jLiveSyncTask.LIVE_SYNC_TASK_TEMPLATE_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jLiveSyncTask liveSyncTask; + + @Override + public LiveSyncTask getLiveSyncTask() { + return liveSyncTask; + } + + @Override + public void setLiveSyncTask(final LiveSyncTask pullTask) { + checkType(pullTask, Neo4jLiveSyncTask.class); + this.liveSyncTask = (Neo4jLiveSyncTask) pullTask; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jInboundTask.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jInboundTask.java new file mode 100644 index 00000000000..ad452912cd3 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jInboundTask.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.neo4j.entity.task; + +import jakarta.validation.constraints.NotNull; +import org.apache.syncope.core.persistence.api.entity.Realm; +import org.apache.syncope.core.persistence.api.entity.task.InboundTask; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRealm; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jInboundTask.NODE) +public abstract class Neo4jInboundTask> + extends Neo4jProvisioningTask implements InboundTask { + + private static final long serialVersionUID = -4690738171334264804L; + + public static final String NODE = "InboundTask"; + + @NotNull + @Relationship(direction = Relationship.Direction.OUTGOING) + private Neo4jRealm destinationRealm; + + @NotNull + private Boolean remediation = false; + + @Override + public Realm getDestinationRealm() { + return destinationRealm; + } + + @Override + public void setDestinationRealm(final Realm destinationRealm) { + checkType(destinationRealm, Neo4jRealm.class); + this.destinationRealm = (Neo4jRealm) destinationRealm; + } + + @Override + public void setRemediation(final boolean remediation) { + this.remediation = remediation; + } + + @Override + public boolean isRemediation() { + return concurrentSettings != null ? true : remediation; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jLiveSyncTask.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jLiveSyncTask.java new file mode 100644 index 00000000000..a06390d9615 --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jLiveSyncTask.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.neo4j.entity.task; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.SortedSet; +import java.util.TreeSet; +import org.apache.syncope.common.lib.types.IdMImplementationType; +import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.persistence.api.entity.task.AnyTemplateLiveSyncTask; +import org.apache.syncope.core.persistence.api.entity.task.LiveSyncTask; +import org.apache.syncope.core.persistence.api.entity.task.SchedTask; +import org.apache.syncope.core.persistence.api.entity.task.TaskExec; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jImplementation; +import org.apache.syncope.core.persistence.neo4j.entity.Neo4jImplementationRelationship; +import org.apache.syncope.core.persistence.neo4j.entity.SortedSetList; +import org.springframework.data.annotation.Transient; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.PostLoad; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jLiveSyncTask.NODE) +public class Neo4jLiveSyncTask extends Neo4jInboundTask implements LiveSyncTask { + + private static final long serialVersionUID = -6265995715460303850L; + + public static final String NODE = "LiveSyncTask"; + + public static final String LIVE_SYNC_MAPPER_REL = "LIVE_SYNC_MAPPER"; + + public static final String LIVE_SYNC_TASK_LIVE_SYNC_ACTIONS_REL = "LIVE_SYNC_TASK_LIVE_SYNC_ACTIONS"; + + public static final String LIVE_SYNC_TASK_TEMPLATE_REL = "LIVE_SYNC_TASK_TEMPLATE"; + + public static final String LIVE_SYNC_TASK_EXEC_REL = "LIVE_SYNC_TASK_EXEC"; + + @Relationship(type = LIVE_SYNC_MAPPER_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jImplementation liveSyncDeltaMapper; + + @Relationship(type = LIVE_SYNC_TASK_LIVE_SYNC_ACTIONS_REL, direction = Relationship.Direction.OUTGOING) + private SortedSet actions = new TreeSet<>(); + + @Transient + private List sortedActions = new SortedSetList<>( + actions, Neo4jImplementationRelationship.builder()); + + @Relationship(type = LIVE_SYNC_TASK_TEMPLATE_REL, direction = Relationship.Direction.INCOMING) + private List templates = new ArrayList<>(); + + @Relationship(type = LIVE_SYNC_TASK_EXEC_REL, direction = Relationship.Direction.INCOMING) + private List executions = new ArrayList<>(); + + @Override + public Implementation getLiveSyncDeltaMapper() { + return liveSyncDeltaMapper; + } + + @Override + public void setLiveSyncDeltaMapper(final Implementation liveSyncDeltaMapper) { + checkType(liveSyncDeltaMapper, Neo4jImplementation.class); + checkImplementationType(liveSyncDeltaMapper, IdMImplementationType.LIVE_SYNC_DELTA_MAPPER); + this.liveSyncDeltaMapper = (Neo4jImplementation) liveSyncDeltaMapper; + } + + @Override + public boolean add(final Implementation action) { + checkType(action, Neo4jImplementation.class); + checkImplementationType(action, IdMImplementationType.INBOUND_ACTIONS); + return sortedActions.contains((Neo4jImplementation) action) || sortedActions.add((Neo4jImplementation) action); + } + + @Override + public List getActions() { + return sortedActions; + } + + @Override + public boolean add(final AnyTemplateLiveSyncTask template) { + checkType(template, Neo4jAnyTemplateLiveSyncTask.class); + return this.templates.add((Neo4jAnyTemplateLiveSyncTask) template); + } + + @Override + public Optional getTemplate(final String anyType) { + return templates.stream(). + filter(template -> anyType != null && anyType.equals(template.getAnyType().getKey())). + findFirst(); + } + + @Override + public List getTemplates() { + return templates; + } + + @Override + protected boolean doAdd(final TaskExec exec) { + return executions.add((Neo4jLiveSyncTaskExec) exec); + } + + @Override + protected Class> executionClass() { + return Neo4jLiveSyncTaskExec.class; + } + + @Override + protected List> executions() { + return executions; + } + + @PostLoad + public void postLoad() { + sortedActions = new SortedSetList<>(actions, Neo4jImplementationRelationship.builder()); + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jLiveSyncTaskExec.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jLiveSyncTaskExec.java new file mode 100644 index 00000000000..fbe0c69875f --- /dev/null +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jLiveSyncTaskExec.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.neo4j.entity.task; + +import org.apache.syncope.core.persistence.api.entity.task.LiveSyncTask; +import org.apache.syncope.core.persistence.api.entity.task.SchedTask; +import org.apache.syncope.core.persistence.api.entity.task.TaskExec; +import org.springframework.data.neo4j.core.schema.Node; +import org.springframework.data.neo4j.core.schema.Relationship; + +@Node(Neo4jLiveSyncTaskExec.NODE) +public class Neo4jLiveSyncTaskExec extends AbstractTaskExec implements TaskExec { + + private static final long serialVersionUID = 1909033231464074554L; + + public static final String NODE = "LiveSyncTaskExec"; + + @Relationship(type = Neo4jLiveSyncTask.LIVE_SYNC_TASK_EXEC_REL, direction = Relationship.Direction.OUTGOING) + private Neo4jLiveSyncTask task; + + @Override + public LiveSyncTask getTask() { + return task; + } + + @Override + public void setTask(final SchedTask task) { + checkType(task, LiveSyncTask.class); + this.task = (Neo4jLiveSyncTask) task; + } +} diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPullTask.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPullTask.java index 23a5f1ff83d..cda05ef0fb0 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPullTask.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jPullTask.java @@ -27,14 +27,12 @@ import org.apache.syncope.common.lib.types.IdMImplementationType; import org.apache.syncope.common.lib.types.PullMode; import org.apache.syncope.core.persistence.api.entity.Implementation; -import org.apache.syncope.core.persistence.api.entity.Realm; import org.apache.syncope.core.persistence.api.entity.task.AnyTemplatePullTask; import org.apache.syncope.core.persistence.api.entity.task.PullTask; import org.apache.syncope.core.persistence.api.entity.task.SchedTask; import org.apache.syncope.core.persistence.api.entity.task.TaskExec; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jImplementation; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jImplementationRelationship; -import org.apache.syncope.core.persistence.neo4j.entity.Neo4jRealm; import org.apache.syncope.core.persistence.neo4j.entity.SortedSetList; import org.springframework.data.annotation.Transient; import org.springframework.data.neo4j.core.schema.Node; @@ -42,7 +40,7 @@ import org.springframework.data.neo4j.core.schema.Relationship; @Node(Neo4jPullTask.NODE) -public class Neo4jPullTask extends Neo4jProvisioningTask implements PullTask { +public class Neo4jPullTask extends Neo4jInboundTask implements PullTask { private static final long serialVersionUID = -4141057723006682563L; @@ -50,7 +48,7 @@ public class Neo4jPullTask extends Neo4jProvisioningTask implements Pu public static final String PULL_TASK_RECON_FILTER_BUIDER_REL = "PULL_TASK_RECON_FILTER_BUIDER"; - public static final String PULL_TASK_PULL_ACTIONS_REL = "PULL_TASK_PULL_ACTIONS"; + public static final String PULL_TASK_INBOUND_ACTIONS_REL = "PULL_TASK_INBOUND_ACTIONS"; public static final String PULL_TASK_TEMPLATE_REL = "PULL_TASK_TEMPLATE"; @@ -62,11 +60,7 @@ public class Neo4jPullTask extends Neo4jProvisioningTask implements Pu @Relationship(type = PULL_TASK_RECON_FILTER_BUIDER_REL, direction = Relationship.Direction.OUTGOING) private Neo4jImplementation reconFilterBuilder; - @NotNull - @Relationship(direction = Relationship.Direction.OUTGOING) - private Neo4jRealm destinationRealm; - - @Relationship(type = PULL_TASK_PULL_ACTIONS_REL, direction = Relationship.Direction.OUTGOING) + @Relationship(type = PULL_TASK_INBOUND_ACTIONS_REL, direction = Relationship.Direction.OUTGOING) private SortedSet actions = new TreeSet<>(); @Transient @@ -79,9 +73,6 @@ public class Neo4jPullTask extends Neo4jProvisioningTask implements Pu @Relationship(type = PULL_TASK_EXEC_REL, direction = Relationship.Direction.INCOMING) private List executions = new ArrayList<>(); - @NotNull - private Boolean remediation = false; - @Override public PullMode getPullMode() { return pullMode; @@ -104,21 +95,10 @@ public void setReconFilterBuilder(final Implementation reconFilterBuilder) { this.reconFilterBuilder = (Neo4jImplementation) reconFilterBuilder; } - @Override - public Realm getDestinationRealm() { - return destinationRealm; - } - - @Override - public void setDestinationRealm(final Realm destinationRealm) { - checkType(destinationRealm, Neo4jRealm.class); - this.destinationRealm = (Neo4jRealm) destinationRealm; - } - @Override public boolean add(final Implementation action) { checkType(action, Neo4jImplementation.class); - checkImplementationType(action, IdMImplementationType.PULL_ACTIONS); + checkImplementationType(action, IdMImplementationType.INBOUND_ACTIONS); return sortedActions.contains((Neo4jImplementation) action) || sortedActions.add((Neo4jImplementation) action); } @@ -145,16 +125,6 @@ public List getTemplates() { return templates; } - @Override - public void setRemediation(final boolean remediation) { - this.remediation = remediation; - } - - @Override - public boolean isRemediation() { - return concurrentSettings != null ? true : remediation; - } - @Override protected boolean doAdd(final TaskExec exec) { return executions.add((Neo4jPullTaskExec) exec); diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jTaskUtils.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jTaskUtils.java index 89ea92a48a6..0a46f1320cb 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jTaskUtils.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jTaskUtils.java @@ -18,20 +18,7 @@ */ package org.apache.syncope.core.persistence.neo4j.entity.task; -import org.apache.syncope.common.lib.to.MacroTaskTO; -import org.apache.syncope.common.lib.to.NotificationTaskTO; -import org.apache.syncope.common.lib.to.PropagationTaskTO; -import org.apache.syncope.common.lib.to.PullTaskTO; -import org.apache.syncope.common.lib.to.PushTaskTO; -import org.apache.syncope.common.lib.to.SchedTaskTO; -import org.apache.syncope.common.lib.to.TaskTO; import org.apache.syncope.common.lib.types.TaskType; -import org.apache.syncope.core.persistence.api.entity.task.MacroTask; -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.PullTask; -import org.apache.syncope.core.persistence.api.entity.task.PushTask; -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; import org.apache.syncope.core.persistence.api.entity.task.TaskUtils; @@ -51,41 +38,6 @@ public TaskType getType() { return type; } - @Override - public > Class taskClass() { - Class result = null; - - switch (type) { - case PROPAGATION: - result = (Class) PropagationTask.class; - break; - - case SCHEDULED: - result = (Class) SchedTask.class; - break; - - case PULL: - result = (Class) PullTask.class; - break; - - case PUSH: - result = (Class) PushTask.class; - break; - - case MACRO: - result = (Class) MacroTask.class; - break; - - case NOTIFICATION: - result = (Class) NotificationTask.class; - break; - - default: - } - - return result; - } - @Override public > T newTask() { T result = null; @@ -99,6 +51,10 @@ public > T newTask() { result = (T) new Neo4jSchedTask(); break; + case LIVE_SYNC: + result = (T) new Neo4jLiveSyncTask(); + break; + case PULL: result = (T) new Neo4jPullTask(); break; @@ -138,6 +94,10 @@ public > E newTaskExec() { result = (E) new Neo4jPropagationTaskExec(); break; + case LIVE_SYNC: + result = (E) new Neo4jLiveSyncTaskExec(); + break; + case PULL: result = (E) new Neo4jPullTaskExec(); break; @@ -166,52 +126,7 @@ public > E newTaskExec() { } @Override - public Class taskTOClass() { - Class result = null; - - switch (type) { - case PROPAGATION: - result = (Class) PropagationTaskTO.class; - break; - - case SCHEDULED: - result = (Class) SchedTaskTO.class; - break; - - case PULL: - result = (Class) PullTaskTO.class; - break; - - case PUSH: - result = (Class) PushTaskTO.class; - break; - - case MACRO: - result = (Class) MacroTaskTO.class; - break; - - case NOTIFICATION: - result = (Class) NotificationTaskTO.class; - break; - - default: - } - - return result; - } - - @Override - public T newTaskTO() { - Class taskClass = taskTOClass(); - try { - return taskClass == null ? null : taskClass.getDeclaredConstructor().newInstance(); - } catch (Exception e) { - return null; - } - } - - @Override - public String getTaskTable() { + public String getTaskStorage() { String result = null; switch (type) { @@ -227,6 +142,10 @@ public String getTaskTable() { result = Neo4jPushTask.NODE; break; + case LIVE_SYNC: + result = Neo4jLiveSyncTask.NODE; + break; + case PULL: result = Neo4jPullTask.NODE; break; @@ -262,6 +181,10 @@ public Class> getTaskEntity() { result = Neo4jPushTask.class; break; + case LIVE_SYNC: + result = Neo4jLiveSyncTask.class; + break; + case PULL: result = Neo4jPullTask.class; break; @@ -281,7 +204,7 @@ public Class> getTaskEntity() { } @Override - public String getTaskExecTable() { + public String getTaskExecStorage() { String result = null; switch (type) { @@ -301,6 +224,10 @@ public String getTaskExecTable() { result = Neo4jPushTaskExec.NODE; break; + case LIVE_SYNC: + result = Neo4jLiveSyncTaskExec.NODE; + break; + case PULL: result = Neo4jPullTaskExec.NODE; break; @@ -336,6 +263,10 @@ public Class> getTaskExecEntity() { result = Neo4jPushTaskExec.class; break; + case LIVE_SYNC: + result = Neo4jLiveSyncTaskExec.class; + break; + case PULL: result = Neo4jPullTaskExec.class; break; diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jTaskUtilsFactory.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jTaskUtilsFactory.java index c8f00f7a356..310218ddb80 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jTaskUtilsFactory.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/entity/task/Neo4jTaskUtilsFactory.java @@ -20,6 +20,7 @@ import java.util.HashMap; import java.util.Map; +import org.apache.syncope.common.lib.to.LiveSyncTaskTO; import org.apache.syncope.common.lib.to.MacroTaskTO; import org.apache.syncope.common.lib.to.NotificationTaskTO; import org.apache.syncope.common.lib.to.PropagationTaskTO; @@ -28,6 +29,7 @@ import org.apache.syncope.common.lib.to.SchedTaskTO; import org.apache.syncope.common.lib.to.TaskTO; import org.apache.syncope.common.lib.types.TaskType; +import org.apache.syncope.core.persistence.api.entity.task.LiveSyncTask; import org.apache.syncope.core.persistence.api.entity.task.MacroTask; import org.apache.syncope.core.persistence.api.entity.task.NotificationTask; import org.apache.syncope.core.persistence.api.entity.task.PropagationTask; @@ -61,7 +63,9 @@ public TaskUtils getInstance(final TaskType type) { @Override public TaskUtils getInstance(final Task task) { TaskType type; - if (task instanceof PullTask) { + if (task instanceof LiveSyncTask) { + type = TaskType.LIVE_SYNC; + } else if (task instanceof PullTask) { type = TaskType.PULL; } else if (task instanceof PushTask) { type = TaskType.PUSH; @@ -89,6 +93,8 @@ public TaskUtils getInstance(final Class taskClass) { type = TaskType.NOTIFICATION; } else if (taskClass == SchedTaskTO.class) { type = TaskType.SCHEDULED; + } else if (taskClass == LiveSyncTaskTO.class) { + type = TaskType.LIVE_SYNC; } else if (taskClass == PullTaskTO.class) { type = TaskType.PULL; } else if (taskClass == PushTaskTO.class) { diff --git a/core/persistence-neo4j/src/main/resources/domains/MasterContent.xml b/core/persistence-neo4j/src/main/resources/domains/MasterContent.xml index a45b09760c5..cd3f3d71ecb 100644 --- a/core/persistence-neo4j/src/main/resources/domains/MasterContent.xml +++ b/core/persistence-neo4j/src/main/resources/domains/MasterContent.xml @@ -41,7 +41,8 @@ under the License. - + confOverride, - final Optional> capabilitiesOverride) { + final Optional> confOverride, + final Optional> capabilitiesOverride) { return null; } diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyImplementationLookup.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyImplementationLookup.java index 47d46597972..fadafe2d13d 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyImplementationLookup.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/DummyImplementationLookup.java @@ -20,15 +20,15 @@ import java.util.Set; import org.apache.syncope.common.lib.policy.AccountRuleConf; +import org.apache.syncope.common.lib.policy.InboundCorrelationRuleConf; 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.ReportConf; import org.apache.syncope.core.provisioning.api.ImplementationLookup; import org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate; import org.apache.syncope.core.provisioning.api.rules.AccountRule; +import org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRule; import org.apache.syncope.core.provisioning.api.rules.PasswordRule; -import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRule; import org.apache.syncope.core.provisioning.api.rules.PushCorrelationRule; import org.apache.syncope.core.spring.policy.DefaultAccountRule; import org.apache.syncope.core.spring.policy.DefaultPasswordRule; @@ -65,8 +65,8 @@ public Class getPasswordRuleClass( } @Override - public Class getPullCorrelationRuleClass( - final Class pullCorrelationRuleConfClass) { + public Class getInboundCorrelationRuleClass( + final Class inboundCorrelationRuleConfClass) { return null; } diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ConnInstanceTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ConnInstanceTest.java index b7f426f472e..29ca773e656 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ConnInstanceTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ConnInstanceTest.java @@ -25,9 +25,8 @@ import static org.junit.jupiter.api.Assertions.fail; import java.io.File; -import java.util.HashSet; +import java.util.ArrayList; import java.util.List; -import java.util.Set; import java.util.stream.Collectors; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.types.ConnConfPropSchema; @@ -108,7 +107,7 @@ public void save() throws ClassNotFoundException { connInstance.setConnRequestTimeout(60); // set the connector configuration using PropertyTO - Set conf = new HashSet<>(); + List conf = new ArrayList<>(); ConnConfPropSchema endpointSchema = new ConnConfPropSchema(); endpointSchema.setName("endpoint"); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ImplementationTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ImplementationTest.java index da8ff2954b9..4b0da853146 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ImplementationTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/ImplementationTest.java @@ -51,16 +51,16 @@ public void findAll() { List implementations = implementationDAO.findAll(); assertFalse(implementations.isEmpty()); - assertEquals(21, implementations.size()); + assertEquals(22, implementations.size()); - implementations = implementationDAO.findByType(IdMImplementationType.PULL_ACTIONS); + implementations = implementationDAO.findByType(IdMImplementationType.INBOUND_ACTIONS); assertEquals(1, implementations.size()); implementations = implementationDAO.findByType(IdMImplementationType.PROPAGATION_ACTIONS); assertEquals(2, implementations.size()); implementations = implementationDAO.findByType(IdRepoImplementationType.TASKJOB_DELEGATE); - assertEquals(7, implementations.size()); + assertEquals(8, implementations.size()); implementations = implementationDAO.findByType(IdRepoImplementationType.REPORT_DELEGATE); assertEquals(1, implementations.size()); @@ -76,7 +76,8 @@ public void findAll() { implementations = implementationDAO.findByType(IdRepoImplementationType.DROPDOWN_VALUE_PROVIDER); assertEquals(1, implementations.size()); - implementations = implementationDAO.findByType(IdMImplementationType.PULL_CORRELATION_RULE); + + implementations = implementationDAO.findByType(IdMImplementationType.INBOUND_CORRELATION_RULE); assertEquals(1, implementations.size()); implementations = implementationDAO.findByType(IdMImplementationType.PUSH_CORRELATION_RULE); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PolicyTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PolicyTest.java index e536514eae0..f1223c26a5d 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PolicyTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/PolicyTest.java @@ -27,8 +27,8 @@ import org.apache.syncope.common.lib.policy.DefaultAccessPolicyConf; import org.apache.syncope.common.lib.policy.DefaultAttrReleasePolicyConf; import org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf; +import org.apache.syncope.common.lib.policy.DefaultInboundCorrelationRuleConf; import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf; -import org.apache.syncope.common.lib.policy.DefaultPullCorrelationRuleConf; import org.apache.syncope.common.lib.policy.DefaultPushCorrelationRuleConf; import org.apache.syncope.common.lib.policy.DefaultTicketExpirationPolicyConf; import org.apache.syncope.common.lib.types.AnyTypeKind; @@ -45,16 +45,16 @@ import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy; import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy; import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.InboundCorrelationRuleEntity; +import org.apache.syncope.core.persistence.api.entity.policy.InboundPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy; import org.apache.syncope.core.persistence.api.entity.policy.Policy; import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy; -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.policy.PushCorrelationRuleEntity; import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy; import org.apache.syncope.core.persistence.neo4j.AbstractTest; -import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRule; +import org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRule; import org.apache.syncope.core.provisioning.api.rules.PushCorrelationRule; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; import org.junit.jupiter.api.Test; @@ -75,16 +75,16 @@ public class PolicyTest extends AbstractTest { @Test public void findAll() { - assertEquals(16, policyDAO.count()); - assertEquals(16, policyDAO.findAll().size()); + assertEquals(17, policyDAO.count()); + assertEquals(17, policyDAO.findAll().size()); assertEquals(1, policyDAO.findAll(AccessPolicy.class).size()); assertEquals(2, policyDAO.findAll(AccountPolicy.class).size()); assertEquals(2, policyDAO.findAll(AttrReleasePolicy.class).size()); assertEquals(2, policyDAO.findAll(AuthPolicy.class).size()); assertEquals(3, policyDAO.findAll(PasswordPolicy.class).size()); - assertEquals(1, policyDAO.findAll(PropagationPolicy.class).size()); - assertEquals(4, policyDAO.findAll(PullPolicy.class).size()); + assertEquals(2, policyDAO.findAll(PropagationPolicy.class).size()); + assertEquals(4, policyDAO.findAll(InboundPolicy.class).size()); assertEquals(1, policyDAO.findAll(PushPolicy.class).size()); } @@ -96,12 +96,12 @@ public void findByKey() { assertEquals("10000", propagationPolicy.getBackOffParams()); assertEquals(5, propagationPolicy.getMaxAttempts()); - PullPolicy pullPolicy = policyDAO.findById( - "880f8553-069b-4aed-9930-2cd53873f544", PullPolicy.class).orElseThrow(); + InboundPolicy inboundPolicy = policyDAO.findById("880f8553-069b-4aed-9930-2cd53873f544", InboundPolicy.class). + orElseThrow(); - PullCorrelationRuleEntity pullCR = pullPolicy.getCorrelationRule(AnyTypeKind.USER.name()).orElseThrow(); - DefaultPullCorrelationRuleConf pullCRConf = - POJOHelper.deserialize(pullCR.getImplementation().getBody(), DefaultPullCorrelationRuleConf.class); + InboundCorrelationRuleEntity pullCR = inboundPolicy.getCorrelationRule(AnyTypeKind.USER.name()).orElseThrow(); + DefaultInboundCorrelationRuleConf pullCRConf = + POJOHelper.deserialize(pullCR.getImplementation().getBody(), DefaultInboundCorrelationRuleConf.class); assertNotNull(pullCRConf); assertEquals(2, pullCRConf.getSchemas().size()); assertTrue(pullCRConf.getSchemas().contains("username")); @@ -152,9 +152,9 @@ public void createPropagation() { @Test public void createPull() { - PullPolicy pullPolicy = entityFactory.newEntity(PullPolicy.class); - pullPolicy.setConflictResolutionAction(ConflictResolutionAction.IGNORE); - pullPolicy.setName("Pull policy"); + InboundPolicy inboundPolicy = entityFactory.newEntity(InboundPolicy.class); + inboundPolicy.setConflictResolutionAction(ConflictResolutionAction.IGNORE); + inboundPolicy.setName("Pull policy"); final String pullURuleName = "net.tirasa.pull.correlation.TirasaURule"; final String pullGRuleName = "net.tirasa.pull.correlation.TirasaGRule"; @@ -162,36 +162,36 @@ public void createPull() { Implementation impl1 = entityFactory.newEntity(Implementation.class); impl1.setKey(pullURuleName); impl1.setEngine(ImplementationEngine.JAVA); - impl1.setType(IdMImplementationType.PULL_CORRELATION_RULE); - impl1.setBody(PullCorrelationRule.class.getName()); + impl1.setType(IdMImplementationType.INBOUND_CORRELATION_RULE); + impl1.setBody(InboundCorrelationRule.class.getName()); impl1 = implementationDAO.save(impl1); - PullCorrelationRuleEntity rule1 = entityFactory.newEntity(PullCorrelationRuleEntity.class); + InboundCorrelationRuleEntity rule1 = entityFactory.newEntity(InboundCorrelationRuleEntity.class); rule1.setAnyType(anyTypeDAO.getUser()); - rule1.setPullPolicy(pullPolicy); + rule1.setInboundPolicy(inboundPolicy); rule1.setImplementation(impl1); - pullPolicy.add(rule1); + inboundPolicy.add(rule1); Implementation impl2 = entityFactory.newEntity(Implementation.class); impl2.setKey(pullGRuleName); impl2.setEngine(ImplementationEngine.JAVA); - impl2.setType(IdMImplementationType.PULL_CORRELATION_RULE); - impl2.setBody(PullCorrelationRule.class.getName()); + impl2.setType(IdMImplementationType.INBOUND_CORRELATION_RULE); + impl2.setBody(InboundCorrelationRule.class.getName()); impl2 = implementationDAO.save(impl2); - PullCorrelationRuleEntity rule2 = entityFactory.newEntity(PullCorrelationRuleEntity.class); + InboundCorrelationRuleEntity rule2 = entityFactory.newEntity(InboundCorrelationRuleEntity.class); rule2.setAnyType(anyTypeDAO.getGroup()); - rule2.setPullPolicy(pullPolicy); + rule2.setInboundPolicy(inboundPolicy); rule2.setImplementation(impl2); - pullPolicy.add(rule2); + inboundPolicy.add(rule2); - pullPolicy = policyDAO.save(pullPolicy); + inboundPolicy = policyDAO.save(inboundPolicy); - assertNotNull(pullPolicy); + assertNotNull(inboundPolicy); assertEquals(pullURuleName, - pullPolicy.getCorrelationRule(AnyTypeKind.USER.name()).get().getImplementation().getKey()); + inboundPolicy.getCorrelationRule(AnyTypeKind.USER.name()).get().getImplementation().getKey()); assertEquals(pullGRuleName, - pullPolicy.getCorrelationRule(AnyTypeKind.GROUP.name()).get().getImplementation().getKey()); + inboundPolicy.getCorrelationRule(AnyTypeKind.GROUP.name()).get().getImplementation().getKey()); } @Test diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/TaskTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/TaskTest.java index b633a088391..74a9706b420 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/TaskTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/TaskTest.java @@ -49,6 +49,7 @@ import org.apache.syncope.core.persistence.api.dao.TaskExecDAO; import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.persistence.api.entity.task.LiveSyncTask; import org.apache.syncope.core.persistence.api.entity.task.MacroTask; import org.apache.syncope.core.persistence.api.entity.task.MacroTaskCommand; import org.apache.syncope.core.persistence.api.entity.task.PropagationData; @@ -60,7 +61,7 @@ import org.apache.syncope.core.persistence.api.entity.task.TaskExec; import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory; import org.apache.syncope.core.persistence.neo4j.AbstractTest; -import org.apache.syncope.core.provisioning.api.pushpull.PullActions; +import org.apache.syncope.core.provisioning.api.pushpull.InboundActions; import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.AttributeBuilder; import org.junit.jupiter.api.Test; @@ -264,15 +265,15 @@ public void savePullTask() { task.setMatchingRule(MatchingRule.UPDATE); task.setUnmatchingRule(UnmatchingRule.PROVISION); - // now adding PullActions - Implementation pullActions = entityFactory.newEntity(Implementation.class); - pullActions.setKey("PullActions" + UUID.randomUUID().toString()); - pullActions.setEngine(ImplementationEngine.JAVA); - pullActions.setType(IdMImplementationType.PULL_ACTIONS); - pullActions.setBody(PullActions.class.getName()); - pullActions = implementationDAO.save(pullActions); + // now adding InboundActions + Implementation inboundActions = entityFactory.newEntity(Implementation.class); + inboundActions.setKey("InboundActions" + UUID.randomUUID().toString()); + inboundActions.setEngine(ImplementationEngine.JAVA); + inboundActions.setType(IdMImplementationType.INBOUND_ACTIONS); + inboundActions.setBody(InboundActions.class.getName()); + inboundActions = implementationDAO.save(inboundActions); - task.add(pullActions); + task.add(inboundActions); // this save() fails because of an invalid Cron Expression try { @@ -301,12 +302,12 @@ public void savePullTask() { } @Test - public void addAndRemovePullActions() { + public void addAndRemoveInboundActions() { Implementation implementation = entityFactory.newEntity(Implementation.class); implementation.setKey(UUID.randomUUID().toString()); implementation.setEngine(ImplementationEngine.JAVA); - implementation.setType(IdMImplementationType.PULL_ACTIONS); - implementation.setBody("TestPullActions"); + implementation.setType(IdMImplementationType.INBOUND_ACTIONS); + implementation.setBody("TestInboundActions"); implementation = implementationDAO.save(implementation); PullTask task = (PullTask) taskDAO.findById(TaskType.PULL, "c41b9b71-9bfa-4f90-89f2-84787def4c5c"). @@ -328,6 +329,27 @@ public void addAndRemovePullActions() { assertTrue(task.getActions().isEmpty()); } + @Test + public void saveLiveSyncTask() { + LiveSyncTask task = entityFactory.newEntity(LiveSyncTask.class); + task.setName("saveLiveSyncTask"); + task.setDescription("LiveSyncTask description"); + task.setActive(true); + task.setJobDelegate(implementationDAO.findById("LiveSyncJobDelegate").orElseThrow()); + task.setDestinationRealm(realmDAO.getRoot()); + task.setCronExpression("BLA BLA"); + task.setMatchingRule(MatchingRule.UPDATE); + task.setUnmatchingRule(UnmatchingRule.PROVISION); + task.setCronExpression(null); + task.setResource(resourceDAO.findById("ws-target-resource-1").orElseThrow()); + + task = taskDAO.save(task); + assertNotNull(task); + + LiveSyncTask actual = (LiveSyncTask) taskDAO.findById(TaskType.LIVE_SYNC, task.getKey()).orElseThrow(); + assertEquals(task, actual); + } + @Test public void addAndRemoveReconFilterBuilder() { Implementation implementation = entityFactory.newEntity(Implementation.class); @@ -453,12 +475,12 @@ public void issueSYNCOPE144() { ExternalResource resource = resourceDAO.findById("ws-target-resource-1").orElseThrow(); assertNotNull(resource); - Implementation pullActions = entityFactory.newEntity(Implementation.class); - pullActions.setKey("syncope144"); - pullActions.setEngine(ImplementationEngine.JAVA); - pullActions.setType(IdMImplementationType.PULL_ACTIONS); - pullActions.setBody(PullActions.class.getName()); - pullActions = implementationDAO.save(pullActions); + Implementation inboundActions = entityFactory.newEntity(Implementation.class); + inboundActions.setKey("syncope144"); + inboundActions.setEngine(ImplementationEngine.JAVA); + inboundActions.setType(IdMImplementationType.INBOUND_ACTIONS); + inboundActions.setBody(InboundActions.class.getName()); + inboundActions = implementationDAO.save(inboundActions); PullTask task = entityFactory.newEntity(PullTask.class); @@ -467,7 +489,7 @@ public void issueSYNCOPE144() { task.setDescription("issueSYNCOPE144 Description"); task.setActive(true); task.setPullMode(PullMode.FULL_RECONCILIATION); - task.add(pullActions); + task.add(inboundActions); task.setMatchingRule(MatchingRule.UPDATE); task.setUnmatchingRule(UnmatchingRule.PROVISION); task.setJobDelegate(implementationDAO.findById("PullJobDelegate").orElseThrow()); diff --git a/core/persistence-neo4j/src/test/resources/domains/MasterContent.xml b/core/persistence-neo4j/src/test/resources/domains/MasterContent.xml index 5944661a808..65835fe2f2d 100644 --- a/core/persistence-neo4j/src/test/resources/domains/MasterContent.xml +++ b/core/persistence-neo4j/src/test/resources/domains/MasterContent.xml @@ -45,6 +45,8 @@ under the License. + - - - - - - - - - + + + + + + + + + @@ -573,57 +575,68 @@ under the License. capabilities='["AUTHENTICATE","CREATE","UPDATE","DELETE","SEARCH","SYNC"]'/> - + + + - - - + - - - - - - - - - + - @@ -632,46 +645,52 @@ under the License. - - - - - + + + @@ -703,7 +722,8 @@ under the License. - + - - + 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 49d2908bb8b..51ff2875045 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 @@ -27,6 +27,7 @@ import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.AttributeDelta; import org.identityconnectors.framework.common.objects.ConnectorObject; +import org.identityconnectors.framework.common.objects.LiveSyncResultsHandler; import org.identityconnectors.framework.common.objects.ObjectClass; import org.identityconnectors.framework.common.objects.ObjectClassInfo; import org.identityconnectors.framework.common.objects.OperationOptions; @@ -188,6 +189,15 @@ public boolean handle(final ConnectorObject object) { */ SyncToken getLatestSyncToken(ObjectClass objectClass); + /** + * Live sync remote objects from a connector instance. + * + * @param objectClass ConnId's object class + * @param handler to be used to handle deltas + * @param options ConnId's OperationOptions + */ + void livesync(ObjectClass objectClass, LiveSyncResultsHandler handler, OperationOptions options); + /** * Get remote object. * diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ConnectorManager.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ConnectorManager.java index 29aa74b3815..cce33352123 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ConnectorManager.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ConnectorManager.java @@ -18,8 +18,9 @@ */ package org.apache.syncope.core.provisioning.api; -import java.util.Collection; +import java.util.List; import java.util.Optional; +import java.util.Set; import org.apache.syncope.common.lib.to.ConnInstanceTO; import org.apache.syncope.common.lib.types.ConnConfProperty; import org.apache.syncope.common.lib.types.ConnectorCapability; @@ -43,8 +44,8 @@ public interface ConnectorManager { */ ConnInstance buildConnInstanceOverride( ConnInstanceTO connInstance, - Collection confOverride, - Optional> capabilitiesOverride); + Optional> confOverride, + Optional> capabilitiesOverride); /** * Create connector from given connector instance. diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ImplementationLookup.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ImplementationLookup.java index ffd465c79ff..7943ea03976 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ImplementationLookup.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ImplementationLookup.java @@ -20,15 +20,15 @@ import java.util.Set; import org.apache.syncope.common.lib.policy.AccountRuleConf; +import org.apache.syncope.common.lib.policy.InboundCorrelationRuleConf; 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.ReportConf; import org.apache.syncope.core.persistence.api.SyncopeCoreLoader; import org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate; import org.apache.syncope.core.provisioning.api.rules.AccountRule; +import org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRule; import org.apache.syncope.core.provisioning.api.rules.PasswordRule; -import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRule; import org.apache.syncope.core.provisioning.api.rules.PushCorrelationRule; public interface ImplementationLookup extends SyncopeCoreLoader { @@ -44,8 +44,8 @@ Class getAccountRuleClass( Class getPasswordRuleClass( Class passwordRuleConfClass); - Class getPullCorrelationRuleClass( - Class pullCorrelationRuleConfClass); + Class getInboundCorrelationRuleClass( + Class inboundCorrelationRuleConfClass); Class getPushCorrelationRuleClass( Class pushCorrelationRuleConfClass); diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/LiveSyncDeltaMapper.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/LiveSyncDeltaMapper.java new file mode 100644 index 00000000000..823d0ba2eee --- /dev/null +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/LiveSyncDeltaMapper.java @@ -0,0 +1,31 @@ +/* + * 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; + +import org.apache.syncope.common.lib.to.OrgUnit; +import org.apache.syncope.common.lib.to.Provision; +import org.identityconnectors.framework.common.objects.LiveSyncDelta; +import org.identityconnectors.framework.common.objects.SyncDelta; + +public interface LiveSyncDeltaMapper { + + SyncDelta map(LiveSyncDelta liveSyncDelta, OrgUnit orgUnit); + + SyncDelta map(LiveSyncDelta liveSyncDelta, Provision provision); +} diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobNamer.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobNamer.java index d031bac6d7d..2457b5c734e 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobNamer.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/JobNamer.java @@ -18,6 +18,7 @@ */ package org.apache.syncope.core.provisioning.api.job; +import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.syncope.common.lib.SyncopeConstants; @@ -30,7 +31,7 @@ public final class JobNamer { private static final Logger LOG = LoggerFactory.getLogger(JobNamer.class); - private static String getKeyFromJobName(final String name, final String pattern, final int prefixLength) { + private static Optional getKeyFromJobName(final String name, final String pattern, final int prefixLength) { String result = null; Matcher jobMatcher = Pattern.compile(pattern).matcher(name); @@ -42,14 +43,14 @@ private static String getKeyFromJobName(final String name, final String pattern, } } - return result; + return Optional.ofNullable(result); } - public static String getTaskKeyFromJobName(final String name) { + public static Optional getTaskKeyFromJobName(final String name) { return getKeyFromJobName(name, "taskJob" + SyncopeConstants.UUID_REGEX, 7); } - public static String getReportKeyFromJobName(final String name) { + public static Optional getReportKeyFromJobName(final String name) { return getKeyFromJobName(name, "reportJob" + SyncopeConstants.UUID_REGEX, 9); } diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/SchedTaskJobDelegate.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/SchedTaskJobDelegate.java index 7d3dc0026ad..0bc86026834 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/SchedTaskJobDelegate.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/SchedTaskJobDelegate.java @@ -27,13 +27,11 @@ public interface SchedTaskJobDelegate { * * @param taskType Type of task to run * @param taskKey Task key to run - * @param dryRun indicates if execution shall be simulated with no actual changes * @param context execution context, can be used to pass parameters to the job * @throws JobExecutionException if anything goes wrong */ void execute( TaskType taskType, String taskKey, - boolean dryRun, JobExecutionContext context) throws JobExecutionException; } diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/StoppableSchedTaskJobDelegate.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/StoppableSchedTaskJobDelegate.java new file mode 100644 index 00000000000..9bb999ae96c --- /dev/null +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/job/StoppableSchedTaskJobDelegate.java @@ -0,0 +1,27 @@ +/* + * 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.job; + +public interface StoppableSchedTaskJobDelegate extends SchedTaskJobDelegate { + + /** + * Request the current Job to stop the execution of the running Task. + */ + void stop(); +} 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/InboundActions.java similarity index 61% rename from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/PullActions.java rename to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/InboundActions.java index 406f9b1a09b..9499c5d33d0 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/InboundActions.java @@ -28,19 +28,19 @@ import org.apache.syncope.common.lib.to.ProvisioningReport; import org.apache.syncope.common.lib.to.RealmTO; import org.apache.syncope.core.provisioning.api.job.JobExecutionException; -import org.identityconnectors.framework.common.objects.SyncDelta; +import org.identityconnectors.framework.common.objects.LiveSyncDelta; /** - * Interface for actions to be performed during pull. - * All methods can throw {@link IgnoreProvisionException} to make the current any object ignored by the pull + * Interface for actions to be performed during inbound. + * All methods can throw {@link IgnoreProvisionException} to make the current any object ignored by the inbound * process. */ -public interface PullActions extends ProvisioningActions { +public interface InboundActions extends ProvisioningActions { /** * Return additional attributes to include in the result from the underlying connector. * - * @param profile profile of the pull being executed. + * @param profile profile of the inbound being executed. * @param orgUnit Realm provisioning information * @return additional attributes to include in the result from the underlying connector */ @@ -51,7 +51,7 @@ default Set moreAttrsToGet(ProvisioningProfile profile, OrgUnit or /** * Return additional attributes to include in the result from the underlying connector. * - * @param profile profile of the pull being executed. + * @param profile profile of the inbound being executed. * @param provision Any provisioning information * @return additional attributes to include in the result from the underlying connector */ @@ -60,206 +60,207 @@ default Set moreAttrsToGet(ProvisioningProfile profile, Provision } /** - * Pre-process the pull information received by the underlying connector, before any internal activity occurs. + * Pre-process the inbound information received by the underlying connector, before any internal activity occurs. * - * @param profile profile of the pull being executed. - * @param delta retrieved pull information - * @return pull information, possibly altered. + * @param sync delta class + * @param profile profile of the inbound being executed. + * @param delta retrieved inbound information + * @return inbound information, possibly altered. */ - default SyncDelta preprocess(ProvisioningProfile profile, SyncDelta delta) { + default T preprocess(ProvisioningProfile profile, T delta) { return delta; } /** - * Action to be executed before to create a pulled entity locally. - * The entity is created locally upon pull in case of the un-matching rule + * Action to be executed before to create a inbounded entity locally. + * The entity is created locally upon inbound in case of the un-matching rule * {@link org.apache.syncope.common.lib.types.UnmatchingRule#PROVISION} (default un-matching rule) is applied. * - * @param profile profile of the pull being executed. - * @param delta retrieved pull information + * @param profile profile of the inbound being executed. + * @param delta retrieved inbound information * @param createReq create request */ default void beforeProvision( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, AnyCR createReq) { } /** * Action to be executed before locally creating a linked account. * - * @param profile profile of the pull being executed. - * @param delta retrieved pull information + * @param profile profile of the inbound being executed. + * @param delta retrieved inbound information * @param linkedAccount create request */ default void beforeProvision( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, LinkedAccountTO linkedAccount) { } /** - * Action to be executed before to create a pulled realm locally. - * The realm is created locally upon pull in case of the un-matching rule + * Action to be executed before to create a inbounded realm locally. + * The realm is created locally upon inbound in case of the un-matching rule * {@link org.apache.syncope.common.lib.types.UnmatchingRule#PROVISION} (default un-matching rule) is applied. * - * @param profile profile of the pull being executed. - * @param delta retrieved pull information + * @param profile profile of the inbound being executed. + * @param delta retrieved inbound information * @param realm realm */ default void beforeProvision( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, RealmTO realm) { } /** - * Action to be executed before creating (and linking to the resource) a pulled entity locally. - * The entity is created locally and linked to the pulled resource upon pull in case of the + * Action to be executed before creating (and linking to the resource) a inbounded entity locally. + * The entity is created locally and linked to the inbounded resource upon inbound in case of the * un-matching rule {@link org.apache.syncope.common.lib.types.UnmatchingRule#ASSIGN} is applied. * - * @param profile profile of the pull being executed. - * @param delta retrieved pull information + * @param profile profile of the inbound being executed. + * @param delta retrieved inbound information * @param createReq create request */ default void beforeAssign( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, AnyCR createReq) { } /** * Action to be executed before locally creating a linked account. * - * @param profile profile of the pull being executed. - * @param delta retrieved pull information + * @param profile profile of the inbound being executed. + * @param delta retrieved inbound information * @param linkedAccount linked account */ default void beforeAssign( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, LinkedAccountTO linkedAccount) { } /** - * Action to be executed before creating (and linking to the resource) a pulled realm locally. - * The realm is created locally and linked to the pulled resource upon pull in case of the + * Action to be executed before creating (and linking to the resource) a inbounded realm locally. + * The realm is created locally and linked to the inbounded resource upon inbound in case of the * un-matching rule {@link org.apache.syncope.common.lib.types.UnmatchingRule#ASSIGN} is applied. * - * @param profile profile of the pull being executed. - * @param delta retrieved pull information + * @param profile profile of the inbound being executed. + * @param delta retrieved inbound information * @param realm realm */ default void beforeAssign( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, RealmTO realm) { } /** - * Action to be executed before unlinking resource from the pulled entity and de-provisioning. - * The entity is unlinked and de-provisioned from the pulled resource upon pull in case of the + * Action to be executed before unlinking resource from the inbounded entity and de-provisioning. + * The entity is unlinked and de-provisioned from the inbounded resource upon inbound in case of the * matching rule {@link org.apache.syncope.common.lib.types.MatchingRule#UNASSIGN} is applied. * - * @param profile profile of the pull being executed. - * @param delta retrieved pull information + * @param profile profile of the inbound being executed. + * @param delta retrieved inbound information * @param entity entity */ default void beforeUnassign( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, EntityTO entity) { } /** * Action to be executed before de-provisioning action only. - * The entity is de-provisioned (without unlinking) from the pulled resource upon pull in case of + * The entity is de-provisioned (without unlinking) from the inbounded resource upon inbound in case of * the matching rule {@link org.apache.syncope.common.lib.types.MatchingRule#DEPROVISION} is applied. * - * @param profile profile of the pull being executed. - * @param delta retrieved pull information + * @param profile profile of the inbound being executed. + * @param delta retrieved inbound information * @param entity entity */ default void beforeDeprovision( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, EntityTO entity) { } /** - * Action to be executed before unlinking resource from the pulled entity. - * The entity is unlinked (without de-provisioning) from the pulled resource upon pull in case of + * Action to be executed before unlinking resource from the inbounded entity. + * The entity is unlinked (without de-provisioning) from the inbounded resource upon inbound in case of * the matching rule {@link org.apache.syncope.common.lib.types.MatchingRule#UNLINK} is applied. * - * @param profile profile of the pull being executed. - * @param delta retrieved pull information + * @param profile profile of the inbound being executed. + * @param delta retrieved inbound information * @param entity entity */ default void beforeUnlink( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, EntityTO entity) { } /** - * Action to be executed before linking resource to the pulled entity. - * The entity is linked (without updating) to the pulled resource upon pull in case of + * Action to be executed before linking resource to the inbounded entity. + * The entity is linked (without updating) to the inbounded resource upon inbound in case of * the matching rule {@link org.apache.syncope.common.lib.types.MatchingRule#LINK} is applied. * - * @param profile profile of the pull being executed. - * @param delta retrieved pull information + * @param profile profile of the inbound being executed. + * @param delta retrieved inbound information * @param entity entity */ default void beforeLink( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, EntityTO entity) { } /** - * Action to be executed before to update a pulled entity locally. - * The entity is updated upon pull in case of the matching rule + * Action to be executed before to update a inbounded entity locally. + * The entity is updated upon inbound in case of the matching rule * {@link org.apache.syncope.common.lib.types.MatchingRule#UPDATE} (default matching rule) is applied. * - * @param profile profile of the pull being executed. - * @param delta retrieved pull information + * @param profile profile of the inbound being executed. + * @param delta retrieved inbound information * @param entity entity * @param anyUR modification * @throws JobExecutionException in case of generic failure. */ default void beforeUpdate( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, EntityTO entity, AnyUR anyUR) throws JobExecutionException { } /** - * Action to be executed before to delete a pulled entity locally. + * Action to be executed before to delete a inbounded entity locally. * - * @param profile profile of the pull being executed. - * @param delta retrieved pull information + * @param profile profile of the inbound being executed. + * @param delta retrieved inbound information * @param entity entity */ default void beforeDelete( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, EntityTO entity) { } /** - * Action to be executed after each local entity pull. + * Action to be executed after each local entity inbound. * - * @param profile profile of the pull being executed. - * @param delta retrieved pull information (may be modified by beforeProvisionTO / beforeUpdate / + * @param profile profile of the inbound being executed. + * @param delta retrieved inbound information (may be modified by beforeProvisionTO / beforeUpdate / * beforeDelete) * @param entity entity - * @param result global pull results at the current pull step + * @param result global inbound results at the current inbound step * @throws JobExecutionException in case of generic failure */ default void after( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, EntityTO entity, ProvisioningReport result) throws JobExecutionException { @@ -267,17 +268,17 @@ default void after( } /** - * Action to be executed in case an exception is thrown during pull. + * Action to be executed in case an exception is thrown during inbound. * - * @param profile profile of the pull being executed. - * @param delta retrieved pull information (may be modified by beforeProvisionTO / beforeUpdate / + * @param profile profile of the inbound being executed. + * @param delta retrieved inbound information (may be modified by beforeProvisionTO / beforeUpdate / * beforeDelete) * @param e the exception thrown * @return an instance of the given exception type is that is to be thrown; {@code NULL} otherwise */ default IgnoreProvisionException onError( ProvisioningProfile profile, - SyncDelta delta, + LiveSyncDelta delta, Exception e) { return null; 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 b0edb1b5313..3cb8e6034a5 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,11 +18,11 @@ */ package org.apache.syncope.core.provisioning.api.pushpull; -import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import org.apache.syncope.common.lib.to.ProvisioningReport; import org.apache.syncope.common.lib.types.ConflictResolutionAction; +import org.apache.syncope.common.lib.types.TaskType; import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask; import org.apache.syncope.core.provisioning.api.Connector; @@ -30,21 +30,36 @@ public class ProvisioningProfile, A extends Provis private final Connector connector; + private final TaskType taskType; + private final T task; - private final List results = new CopyOnWriteArrayList<>(); + private final ConflictResolutionAction conflictResolutionAction; - private boolean dryRun; + private final String executor; - private ConflictResolutionAction conflictResolutionAction; + private final boolean dryRun; - private String executor; + private final List actions; + + private final List results = new CopyOnWriteArrayList<>(); - private final List actions = new ArrayList<>(); + public ProvisioningProfile( + final Connector connector, + final TaskType taskType, + final T task, + final ConflictResolutionAction conflictResolutionAction, + final List actions, + final String executor, + final boolean dryRun) { - public ProvisioningProfile(final Connector connector, final T task) { this.connector = connector; + this.taskType = taskType; this.task = task; + this.conflictResolutionAction = conflictResolutionAction; + this.actions = actions; + this.executor = executor; + this.dryRun = dryRun; } public Connector getConnector() { @@ -55,35 +70,27 @@ public T getTask() { return task; } - public List getResults() { - return results; - } - public boolean isDryRun() { return dryRun; } - public void setDryRun(final boolean dryRun) { - this.dryRun = dryRun; - } - public ConflictResolutionAction getConflictResolutionAction() { return conflictResolutionAction; } - public void setConflictResolutionAction(final ConflictResolutionAction conflictResolutionAction) { - this.conflictResolutionAction = conflictResolutionAction; - } - public String getExecutor() { return executor; } - public void setExecutor(final String executor) { - this.executor = executor; - } - public List getActions() { return actions; } + + public List getResults() { + return results; + } + + public String getContext() { + return taskType + " Task " + task.getKey() + " '" + task.getName() + "'"; + } } diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopePullResultHandler.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopePullResultHandler.java index 6092987bd28..b02d4fadb95 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopePullResultHandler.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopePullResultHandler.java @@ -22,7 +22,7 @@ import org.identityconnectors.framework.common.objects.SyncDelta; import org.identityconnectors.framework.common.objects.SyncResultsHandler; -public interface SyncopePullResultHandler extends SyncopeResultHandler, SyncResultsHandler { +public interface SyncopePullResultHandler extends SyncopeResultHandler, SyncResultsHandler { @Override boolean handle(SyncDelta delta); diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeResultHandler.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeResultHandler.java index 2b4975cf88d..75263e096f3 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeResultHandler.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/SyncopeResultHandler.java @@ -23,4 +23,6 @@ public interface SyncopeResultHandler, A extends ProvisioningActions> { void setProfile(ProvisioningProfile profile); + + void stop(); } diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/SyncopeStreamPullExecutor.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/SyncopeStreamPullExecutor.java index b31d8912c54..5db630005c6 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/SyncopeStreamPullExecutor.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/stream/SyncopeStreamPullExecutor.java @@ -34,7 +34,7 @@ List pull( String keyColumn, List columns, ConflictResolutionAction conflictResolutionAction, - String pullCorrelationRule, + String inboundCorrelationRule, Connector connector, PullTaskTO pullTaskTO, String executor) throws JobExecutionException; diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/rules/PullCorrelationRule.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/rules/InboundCorrelationRule.java similarity index 73% rename from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/rules/PullCorrelationRule.java rename to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/rules/InboundCorrelationRule.java index c8a9c62b5bf..65b50e28fd2 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/rules/PullCorrelationRule.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/rules/InboundCorrelationRule.java @@ -19,22 +19,22 @@ package org.apache.syncope.core.provisioning.api.rules; import java.util.Optional; -import org.apache.syncope.common.lib.policy.PullCorrelationRuleConf; +import org.apache.syncope.common.lib.policy.InboundCorrelationRuleConf; import org.apache.syncope.common.lib.to.Provision; import org.apache.syncope.common.lib.types.MatchType; import org.apache.syncope.core.persistence.api.dao.search.SearchCond; import org.apache.syncope.core.persistence.api.entity.Any; -import org.identityconnectors.framework.common.objects.SyncDelta; +import org.identityconnectors.framework.common.objects.LiveSyncDelta; /** - * Interface for correlation rule to be evaluated during PullJob execution. + * Interface for correlation rule to be evaluated during inbound task execution. */ @FunctionalInterface -public interface PullCorrelationRule { +public interface InboundCorrelationRule { - PullMatch NO_MATCH = new PullMatch(MatchType.ANY, null); + InboundMatch NO_MATCH = new InboundMatch(MatchType.ANY, null); - default void setConf(PullCorrelationRuleConf conf) { + default void setConf(InboundCorrelationRuleConf conf) { } /** @@ -44,11 +44,11 @@ default void setConf(PullCorrelationRuleConf conf) { * @param provision resource provision * @return search condition. */ - SearchCond getSearchCond(SyncDelta syncDelta, Provision provision); + SearchCond getSearchCond(LiveSyncDelta syncDelta, Provision provision); /** * Create matching information for the given Any, found matching for the given - * {@link SyncDelta} and {@link Provision}. + * {@link LiveSyncDelta} and {@link Provision}. * For users, this might end with creating / updating / deleting a * {@link org.apache.syncope.core.persistence.api.entity.user.LinkedAccount}. * @@ -57,13 +57,13 @@ default void setConf(PullCorrelationRuleConf conf) { * @param provision resource provision * @return matching information */ - default PullMatch matching(Any any, SyncDelta syncDelta, Provision provision) { - return new PullMatch(MatchType.ANY, any); + default InboundMatch matching(Any any, LiveSyncDelta syncDelta, Provision provision) { + return new InboundMatch(MatchType.ANY, any); } /** * Optionally create matching information in case no matching Any was found for the given - * {@link SyncDelta} and {@link Provision}. + * {@link LiveSyncDelta} and {@link Provision}. * For users, this might end with creating a * {@link org.apache.syncope.core.persistence.api.entity.user.LinkedAccount}. * @@ -71,7 +71,7 @@ default PullMatch matching(Any any, SyncDelta syncDelta, Provision provision) * @param provision resource provision * @return matching information */ - default Optional unmatching(SyncDelta syncDelta, Provision provision) { + default Optional unmatching(LiveSyncDelta syncDelta, Provision provision) { return Optional.of(NO_MATCH); } } diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/rules/PullCorrelationRuleConfClass.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/rules/InboundCorrelationRuleConfClass.java similarity index 85% rename from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/rules/PullCorrelationRuleConfClass.java rename to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/rules/InboundCorrelationRuleConfClass.java index c94e4be664f..aaf47f39572 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/rules/PullCorrelationRuleConfClass.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/rules/InboundCorrelationRuleConfClass.java @@ -22,12 +22,12 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import org.apache.syncope.common.lib.policy.PullCorrelationRuleConf; +import org.apache.syncope.common.lib.policy.InboundCorrelationRuleConf; @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) -public @interface PullCorrelationRuleConfClass { +public @interface InboundCorrelationRuleConfClass { - Class value(); + Class value(); } diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/rules/PullMatch.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/rules/InboundMatch.java similarity index 89% rename from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/rules/PullMatch.java rename to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/rules/InboundMatch.java index e70c2eb5024..3b243c11b8d 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/rules/PullMatch.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/rules/InboundMatch.java @@ -26,7 +26,7 @@ import org.apache.syncope.core.persistence.api.entity.Entity; import org.apache.syncope.core.persistence.api.entity.user.LinkedAccount; -public final class PullMatch implements Serializable { +public final class InboundMatch implements Serializable { private static final long serialVersionUID = 6515473131174179932L; @@ -36,13 +36,13 @@ public final class PullMatch implements Serializable { private LinkedAccount linkedAccount; - public PullMatch(final MatchType matchTarget, final Entity entity) { + public InboundMatch(final MatchType matchTarget, final Entity entity) { this.matchTarget = matchTarget; if (entity instanceof Any) { any = (Any) entity; - } else if (entity instanceof LinkedAccount) { - linkedAccount = (LinkedAccount) entity; + } else if (entity instanceof LinkedAccount linkedAccount1) { + linkedAccount = linkedAccount1; } } @@ -78,7 +78,7 @@ public boolean equals(final Object obj) { if (getClass() != obj.getClass()) { return false; } - final PullMatch other = (PullMatch) obj; + final InboundMatch other = (InboundMatch) obj; return new EqualsBuilder(). append(matchTarget, other.matchTarget). append(any, other.any). @@ -88,7 +88,7 @@ public boolean equals(final Object obj) { @Override public String toString() { - return "PullMatch{" + return "InboundMatch{" + "matchTarget=" + matchTarget + ", any=" + any + ", linkedAccount=" + linkedAccount diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/job/JobNamerTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/job/JobNamerTest.java index ef8a1a5e196..1195ed94639 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/job/JobNamerTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/job/JobNamerTest.java @@ -19,7 +19,7 @@ package org.apache.syncope.core.provisioning.api.job; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.when; import java.util.UUID; @@ -36,21 +36,21 @@ public class JobNamerTest extends AbstractTest { @Test public void getTaskKeyFromJobName() { name = "testName"; - assertNull(JobNamer.getTaskKeyFromJobName(name)); + assertTrue(JobNamer.getTaskKeyFromJobName(name).isEmpty()); String uuid = UUID.randomUUID().toString(); name = String.format("taskJob%s", uuid); - assertEquals(uuid, JobNamer.getTaskKeyFromJobName(name)); + assertEquals(uuid, JobNamer.getTaskKeyFromJobName(name).orElseThrow()); } @Test public void getReportKeyFromJobName() { name = "testName"; - assertNull(JobNamer.getTaskKeyFromJobName(name)); + assertTrue(JobNamer.getTaskKeyFromJobName(name).isEmpty()); String uuid = UUID.randomUUID().toString(); name = String.format("reportJob%s", uuid); - assertEquals(uuid, JobNamer.getReportKeyFromJobName(name)); + assertEquals(uuid, JobNamer.getReportKeyFromJobName(name).orElseThrow()); } @Test diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfileTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfileTest.java index c973caeee34..a82b16ec3ba 100644 --- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfileTest.java +++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfileTest.java @@ -20,9 +20,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; -import java.util.ArrayList; +import java.util.List; import org.apache.syncope.common.lib.types.ConflictResolutionAction; +import org.apache.syncope.common.lib.types.TaskType; import org.apache.syncope.core.persistence.api.entity.task.PushTask; import org.apache.syncope.core.provisioning.api.AbstractTest; import org.apache.syncope.core.provisioning.api.Connector; @@ -36,20 +38,20 @@ public void test( final @Mock Connector connector, final @Mock PushTask pushTask) { - boolean dryRun = false; - ConflictResolutionAction conflictResolutionAction = ConflictResolutionAction.FIRSTMATCH; - ProvisioningProfile profile; - profile = new ProvisioningProfile<>(connector, pushTask); + ProvisioningProfile profile = new ProvisioningProfile<>( + connector, + TaskType.PUSH, + pushTask, + ConflictResolutionAction.FIRSTMATCH, + List.of(), + "executor", + false); assertEquals(connector, profile.getConnector()); assertEquals(pushTask, profile.getTask()); - assertEquals(new ArrayList<>(), profile.getResults()); - assertEquals(new ArrayList<>(), profile.getActions()); - - profile.setDryRun(dryRun); assertFalse(profile.isDryRun()); - - profile.setConflictResolutionAction(conflictResolutionAction); - assertEquals(conflictResolutionAction, profile.getConflictResolutionAction()); + assertEquals(ConflictResolutionAction.FIRSTMATCH, profile.getConflictResolutionAction()); + assertTrue(profile.getActions().isEmpty()); + assertTrue(profile.getResults().isEmpty()); } } 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 fcae6cc3993..b2f147b7aff 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 @@ -44,6 +44,7 @@ import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.AttributeDelta; import org.identityconnectors.framework.common.objects.ConnectorObject; +import org.identityconnectors.framework.common.objects.LiveSyncResultsHandler; import org.identityconnectors.framework.common.objects.ObjectClass; import org.identityconnectors.framework.common.objects.ObjectClassInfo; import org.identityconnectors.framework.common.objects.OperationOptions; @@ -296,7 +297,10 @@ public void delete( } @Override - public void sync(final ObjectClass objectClass, final SyncToken token, final SyncResultsHandler handler, + public void sync( + final ObjectClass objectClass, + final SyncToken token, + final SyncResultsHandler handler, final OperationOptions options) { if (connInstance.getCapabilities().contains(ConnectorCapability.SYNC)) { @@ -335,6 +339,20 @@ public SyncToken getLatestSyncToken(final ObjectClass objectClass) { return result; } + @Override + public void livesync( + final ObjectClass objectClass, + final LiveSyncResultsHandler handler, + final OperationOptions options) { + + if (connInstance.getCapabilities().contains(ConnectorCapability.LIVE_SYNC)) { + connector.livesync(objectClass, handler, options); + } else { + LOG.info("livesync was attempted, although the connector only has these capabilities: {}. No action.", + connInstance.getCapabilities()); + } + } + @Override public void fullReconciliation( final ObjectClass objectClass, diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnectorManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnectorManager.java index 5dee7c7258e..2211940fc0d 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnectorManager.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultConnectorManager.java @@ -18,9 +18,9 @@ */ package org.apache.syncope.core.provisioning.java; -import java.util.Collection; +import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Optional; @@ -107,8 +107,8 @@ public Connector getConnector(final ExternalResource resource) { @Override public ConnInstance buildConnInstanceOverride( final ConnInstanceTO connInstance, - final Collection confOverride, - final Optional> capabilitiesOverride) { + final Optional> confOverride, + final Optional> capabilitiesOverride) { ConnInstance override = entityFactory.newEntity(ConnInstance.class); override.setAdminRealm(realmSearchDAO.findByFullPath(connInstance.getAdminRealm()).orElseGet(() -> { @@ -126,7 +126,7 @@ public ConnInstance buildConnInstanceOverride( override.setConnRequestTimeout(connInstance.getConnRequestTimeout()); Map overridable = new HashMap<>(); - Set conf = new HashSet<>(); + List conf = new ArrayList<>(); override.getConf().forEach(prop -> { if (prop.isOverridable()) { @@ -137,12 +137,12 @@ public ConnInstance buildConnInstanceOverride( }); // add override properties - confOverride.stream(). + confOverride.ifPresent(co -> co.stream(). filter(prop -> overridable.containsKey(prop.getSchema().getName()) && !prop.getValues().isEmpty()). forEach(prop -> { conf.add(prop); overridable.remove(prop.getSchema().getName()); - }); + })); // add override properties not substituted conf.addAll(overridable.values()); @@ -155,9 +155,8 @@ public ConnInstance buildConnInstanceOverride( override.getCapabilities().addAll(capabilities); }); - if (connInstance.getPoolConf() != null) { - override.setPoolConf(ConnPoolConfUtils.getConnPoolConf(connInstance.getPoolConf())); - } + Optional.ofNullable(connInstance.getPoolConf()). + ifPresent(pc -> override.setPoolConf(ConnPoolConfUtils.getConnPoolConf(pc))); return override; } @@ -178,7 +177,7 @@ public void registerConnector(final ExternalResource resource) { ConnInstance connInstance = buildConnInstanceOverride( connInstanceDataBinder.getConnInstanceTO(resource.getConnector()), resource.getConfOverride(), - resource.isOverrideCapabilities() ? Optional.of(resource.getCapabilitiesOverride()) : Optional.empty()); + resource.getCapabilitiesOverride()); Connector connector = createConnector(connInstance); LOG.debug("Connector to be registered: {}", connector); 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 4ff60ae5a46..6c9e4adfb9d 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 @@ -163,6 +163,7 @@ import org.apache.syncope.core.provisioning.java.propagation.DefaultPropagationManager; import org.apache.syncope.core.provisioning.java.propagation.PriorityPropagationTaskExecutor; import org.apache.syncope.core.provisioning.java.pushpull.InboundMatcher; +import org.apache.syncope.core.provisioning.java.pushpull.LiveSyncTaskExecSaver; 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.TemplateUtils; @@ -695,6 +696,18 @@ public NotificationJob notificationJob( return new NotificationJob(securityProperties, domainHolder, delegate); } + @ConditionalOnMissingBean + @Bean + public LiveSyncTaskExecSaver liveSyncTaskExecSaver( + final TaskDAO taskDAO, + final TaskExecDAO taskExecDAO, + final TaskUtilsFactory taskUtilsFactory, + final NotificationManager notificationManager, + final AuditManager auditManager) { + + return new LiveSyncTaskExecSaver(taskDAO, taskExecDAO, taskUtilsFactory, notificationManager, auditManager); + } + @ConditionalOnMissingBean @Bean public AccessTokenDataBinder accessTokenDataBinder( diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java index 34f0f1020d9..64afec9f61d 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ConnInstanceDataBinderImpl.java @@ -20,8 +20,9 @@ import java.net.URI; import java.util.Collection; -import java.util.Collections; import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.common.lib.SyncopeClientException; import org.apache.syncope.common.lib.to.ConnInstanceTO; @@ -33,7 +34,6 @@ import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.entity.ConnInstance; import org.apache.syncope.core.persistence.api.entity.EntityFactory; -import org.apache.syncope.core.persistence.api.entity.Realm; import org.apache.syncope.core.persistence.api.utils.ConnPoolConfUtils; import org.apache.syncope.core.provisioning.api.ConnIdBundleManager; import org.apache.syncope.core.provisioning.api.data.ConnInstanceDataBinder; @@ -101,20 +101,16 @@ public ConnInstance getConnInstance(final ConnInstanceTO connInstanceTO) { connInstance.setConnRequestTimeout(connInstanceTO.getConnRequestTimeout()); connInstance.getCapabilities().addAll(connInstanceTO.getCapabilities()); - if (connInstanceTO.getAdminRealm() != null) { - connInstance.setAdminRealm(realmSearchDAO.findByFullPath(connInstanceTO.getAdminRealm()). - orElseThrow(() -> new NotFoundException("Realm " + connInstanceTO.getAdminRealm()))); - } + Optional.ofNullable(connInstanceTO.getAdminRealm()). + ifPresent(r -> connInstance.setAdminRealm(realmSearchDAO.findByFullPath(r).orElse(null))); if (connInstance.getAdminRealm() == null) { sce.getElements().add("Invalid or null realm specified: " + connInstanceTO.getAdminRealm()); } - if (connInstanceTO.getLocation() != null) { - connInstance.setLocation(connInstanceTO.getLocation()); - } + + Optional.ofNullable(connInstanceTO.getLocation()).ifPresent(connInstance::setLocation); connInstance.setConf(connInstanceTO.getConf()); - if (connInstanceTO.getPoolConf() != null) { - connInstance.setPoolConf(ConnPoolConfUtils.getConnPoolConf(connInstanceTO.getPoolConf())); - } + Optional.ofNullable(connInstanceTO.getPoolConf()). + ifPresent(conf -> connInstance.setPoolConf(ConnPoolConfUtils.getConnPoolConf(conf))); // Throw exception if there is at least one element set if (!sce.isEmpty()) { @@ -126,57 +122,32 @@ public ConnInstance getConnInstance(final ConnInstanceTO connInstanceTO) { @Override public ConnInstance update(final ConnInstanceTO connInstanceTO) { - ConnInstance connInstance = connInstanceDAO.authFind(connInstanceTO.getKey()); - if (connInstance == null) { - throw new NotFoundException("Connector '" + connInstanceTO.getKey() + '\''); - } + ConnInstance connInstance = Optional.ofNullable(connInstanceDAO.authFind(connInstanceTO.getKey())). + orElseThrow(() -> new NotFoundException("Connector '" + connInstanceTO.getKey() + '\'')); connInstance.getCapabilities().clear(); connInstance.getCapabilities().addAll(connInstanceTO.getCapabilities()); - if (connInstanceTO.getAdminRealm() != null) { - Realm realm = realmSearchDAO.findByFullPath(connInstanceTO.getAdminRealm()). - orElseThrow(() -> { - SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRealm); - sce.getElements().add("Invalid or null realm specified: " + connInstanceTO.getAdminRealm()); - return sce; - }); - connInstance.setAdminRealm(realm); - } - - if (connInstanceTO.getLocation() != null) { - connInstance.setLocation(connInstanceTO.getLocation()); - } - - if (connInstanceTO.getBundleName() != null) { - connInstance.setBundleName(connInstanceTO.getBundleName()); - } - - if (connInstanceTO.getVersion() != null) { - connInstance.setVersion(connInstanceTO.getVersion()); - } - - if (connInstanceTO.getConnectorName() != null) { - connInstance.setConnectorName(connInstanceTO.getConnectorName()); - } - - if (connInstanceTO.getConf() != null && !connInstanceTO.getConf().isEmpty()) { - connInstance.setConf(connInstanceTO.getConf()); - } - - if (connInstanceTO.getDisplayName() != null) { - connInstance.setDisplayName(connInstanceTO.getDisplayName()); - } - - if (connInstanceTO.getConnRequestTimeout() != null) { - connInstance.setConnRequestTimeout(connInstanceTO.getConnRequestTimeout()); - } - - if (connInstanceTO.getPoolConf() == null) { - connInstance.setPoolConf(null); - } else { - connInstance.setPoolConf(ConnPoolConfUtils.getConnPoolConf(connInstanceTO.getPoolConf())); - } + Optional.ofNullable(connInstanceTO.getAdminRealm()). + ifPresent(r -> connInstance.setAdminRealm(realmSearchDAO.findByFullPath(r). + orElseThrow(() -> { + SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRealm); + sce.getElements().add("Invalid or null realm specified: " + connInstanceTO.getAdminRealm()); + return sce; + }))); + + Optional.ofNullable(connInstanceTO.getLocation()).ifPresent(connInstance::setLocation); + Optional.ofNullable(connInstanceTO.getBundleName()).ifPresent(connInstance::setBundleName); + Optional.ofNullable(connInstanceTO.getVersion()).ifPresent(connInstance::setVersion); + Optional.ofNullable(connInstanceTO.getConnectorName()).ifPresent(connInstance::setConnectorName); + Optional.ofNullable(connInstanceTO.getDisplayName()).ifPresent(connInstance::setDisplayName); + Optional.ofNullable(connInstanceTO.getConf()). + filter(Predicate.not(Collection::isEmpty)). + ifPresent(connInstance::setConf); + Optional.ofNullable(connInstanceTO.getConnRequestTimeout()).ifPresent(connInstance::setConnRequestTimeout); + Optional.ofNullable(connInstanceTO.getPoolConf()).ifPresentOrElse( + conf -> connInstance.setPoolConf(ConnPoolConfUtils.getConnPoolConf(conf)), + () -> connInstance.setPoolConf(null)); return connInstance; } @@ -196,8 +167,8 @@ public ConnConfPropSchema build(final ConfigurationProperty property) { if (property.getValue() != null) { if (property.getValue().getClass().isArray()) { connConfPropSchema.getDefaultValues().addAll(List.of((Object[]) property.getValue())); - } else if (property.getValue() instanceof Collection) { - connConfPropSchema.getDefaultValues().addAll((Collection) property.getValue()); + } else if (property.getValue() instanceof Collection collection) { + connConfPropSchema.getDefaultValues().addAll(collection); } else { connConfPropSchema.getDefaultValues().add(property.getValue()); } @@ -246,8 +217,6 @@ public ConnInstanceTO getConnInstanceTO(final ConnInstance connInstance) { connInstanceTO.setLocation(connInstance.getLocation()); } - Collections.sort(connInstanceTO.getConf()); - connInstanceTO.setPoolConf(connInstance.getPoolConf()); return connInstanceTO; diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ImplementationDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ImplementationDataBinderImpl.java index 53eec0b341b..c56326a6ea8 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ImplementationDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ImplementationDataBinderImpl.java @@ -104,7 +104,7 @@ public void update(final Implementation implementation, final ImplementationTO i case IdRepoImplementationType.ACCOUNT_RULE: case IdRepoImplementationType.PASSWORD_RULE: - case IdMImplementationType.PULL_CORRELATION_RULE: + case IdMImplementationType.INBOUND_CORRELATION_RULE: case IdMImplementationType.PUSH_CORRELATION_RULE: RuleConf rule = POJOHelper.deserialize(implementation.getBody(), RuleConf.class); if (rule == null) { diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java index 271662aeea4..d0c0860556b 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java @@ -22,10 +22,10 @@ import org.apache.syncope.common.lib.policy.AccountPolicyTO; import org.apache.syncope.common.lib.policy.AttrReleasePolicyTO; import org.apache.syncope.common.lib.policy.AuthPolicyTO; +import org.apache.syncope.common.lib.policy.InboundPolicyTO; import org.apache.syncope.common.lib.policy.PasswordPolicyTO; import org.apache.syncope.common.lib.policy.PolicyTO; import org.apache.syncope.common.lib.policy.PropagationPolicyTO; -import org.apache.syncope.common.lib.policy.PullPolicyTO; import org.apache.syncope.common.lib.policy.PushPolicyTO; import org.apache.syncope.common.lib.policy.TicketExpirationPolicyTO; import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; @@ -41,11 +41,11 @@ import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy; import org.apache.syncope.core.persistence.api.entity.policy.AttrReleasePolicy; import org.apache.syncope.core.persistence.api.entity.policy.AuthPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.InboundCorrelationRuleEntity; +import org.apache.syncope.core.persistence.api.entity.policy.InboundPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy; import org.apache.syncope.core.persistence.api.entity.policy.Policy; import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy; -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.policy.PushCorrelationRuleEntity; import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; import org.apache.syncope.core.persistence.api.entity.policy.TicketExpirationPolicy; @@ -129,24 +129,24 @@ protected T getPolicy(final T policy, final PolicyTO policyTO propagationPolicy.setBackOffStrategy(propagationPolicyTO.getBackOffStrategy()); propagationPolicy.setBackOffParams(propagationPolicyTO.getBackOffParams()); propagationPolicy.setMaxAttempts(propagationPolicyTO.getMaxAttempts()); - } else if (policyTO instanceof PullPolicyTO pullPolicyTO) { + } else if (policyTO instanceof InboundPolicyTO inboundPolicyTO) { if (result == null) { - result = (T) entityFactory.newEntity(PullPolicy.class); + result = (T) entityFactory.newEntity(InboundPolicy.class); } - PullPolicy pullPolicy = PullPolicy.class.cast(result); + InboundPolicy inboundPolicy = InboundPolicy.class.cast(result); - pullPolicy.setConflictResolutionAction(pullPolicyTO.getConflictResolutionAction()); + inboundPolicy.setConflictResolutionAction(inboundPolicyTO.getConflictResolutionAction()); - pullPolicyTO.getCorrelationRules().forEach((type, impl) -> anyTypeDAO.findById(type).ifPresentOrElse( + inboundPolicyTO.getCorrelationRules().forEach((type, impl) -> anyTypeDAO.findById(type).ifPresentOrElse( anyType -> { - PullCorrelationRuleEntity correlationRule = pullPolicy. + InboundCorrelationRuleEntity correlationRule = inboundPolicy. getCorrelationRule(anyType.getKey()).orElse(null); if (correlationRule == null) { - correlationRule = entityFactory.newEntity(PullCorrelationRuleEntity.class); + correlationRule = entityFactory.newEntity(InboundCorrelationRuleEntity.class); correlationRule.setAnyType(anyType); - correlationRule.setPullPolicy(pullPolicy); - pullPolicy.add(correlationRule); + correlationRule.setInboundPolicy(inboundPolicy); + inboundPolicy.add(correlationRule); } Implementation rule = implementationDAO.findById(impl). @@ -155,7 +155,7 @@ protected T getPolicy(final T policy, final PolicyTO policyTO }, () -> LOG.debug("Invalid AnyType {} specified, ignoring...", type))); // remove all rules not contained in the TO - pullPolicy.getCorrelationRules().removeIf(anyFilter -> !pullPolicyTO.getCorrelationRules(). + inboundPolicy.getCorrelationRules().removeIf(anyFilter -> !inboundPolicyTO.getCorrelationRules(). containsKey(anyFilter.getAnyType().getKey())); } else if (policyTO instanceof PushPolicyTO pushPolicyTO) { if (result == null) { @@ -280,13 +280,13 @@ public T getPolicyTO(final Policy policy) { propagationPolicyTO.setBackOffStrategy(propagationPolicy.getBackOffStrategy()); propagationPolicyTO.setBackOffParams(propagationPolicy.getBackOffParams()); propagationPolicyTO.setMaxAttempts(propagationPolicy.getMaxAttempts()); - } else if (policy instanceof PullPolicy pullPolicy) { - PullPolicyTO pullPolicyTO = new PullPolicyTO(); - policyTO = (T) pullPolicyTO; + } else if (policy instanceof InboundPolicy inboundPolicy) { + InboundPolicyTO inboundPolicyTO = new InboundPolicyTO(); + policyTO = (T) inboundPolicyTO; - pullPolicyTO.setConflictResolutionAction(pullPolicy.getConflictResolutionAction()); - pullPolicy.getCorrelationRules(). - forEach(rule -> pullPolicyTO.getCorrelationRules(). + inboundPolicyTO.setConflictResolutionAction(inboundPolicy.getConflictResolutionAction()); + inboundPolicy.getCorrelationRules(). + forEach(rule -> inboundPolicyTO.getCorrelationRules(). put(rule.getAnyType().getKey(), rule.getImplementation().getKey())); } else if (policy instanceof PushPolicy pushPolicy) { PushPolicyTO pushPolicyTO = new PushPolicyTO(); 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 18c70ab728d..8fb580cf14c 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 @@ -19,8 +19,6 @@ package org.apache.syncope.core.provisioning.java.data; import java.text.ParseException; -import java.util.Collections; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Optional; @@ -57,9 +55,9 @@ 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.AccountPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.InboundPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PropagationPolicy; -import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy; import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy; import org.apache.syncope.core.provisioning.api.IntAttrName; import org.apache.syncope.core.provisioning.api.IntAttrNameParser; @@ -361,8 +359,8 @@ public ExternalResource update(final ExternalResource resource, final ResourceTO resource.setPropagationPolicy(resourceTO.getPropagationPolicy() == null ? null : policyDAO.findById(resourceTO.getPropagationPolicy(), PropagationPolicy.class).orElse(null)); - resource.setPullPolicy(resourceTO.getPullPolicy() == null - ? null : policyDAO.findById(resourceTO.getPullPolicy(), PullPolicy.class).orElse(null)); + resource.setInboundPolicy(resourceTO.getInboundPolicy() == null + ? null : policyDAO.findById(resourceTO.getInboundPolicy(), InboundPolicy.class).orElse(null)); resource.setPushPolicy(resourceTO.getPushPolicy() == null ? null : policyDAO.findById(resourceTO.getPushPolicy(), PushPolicy.class).orElse(null)); @@ -376,11 +374,11 @@ public ExternalResource update(final ExternalResource resource, final ResourceTO Implementation.class.getSimpleName(), resourceTO.getProvisionSorter())); } - resource.setConfOverride(new HashSet<>(resourceTO.getConfOverride())); + resource.setConfOverride( + Optional.ofNullable(resourceTO.getConfOverride()).orElse(Optional.empty())); - resource.setOverrideCapabilities(resourceTO.isOverrideCapabilities()); - resource.getCapabilitiesOverride().clear(); - resource.getCapabilitiesOverride().addAll(resourceTO.getCapabilitiesOverride()); + resource.setCapabilitiesOverride( + Optional.ofNullable(resourceTO.getCapabilitiesOverride()).orElse(Optional.empty())); resourceTO.getPropagationActions().forEach(key -> implementationDAO.findById(key).ifPresentOrElse( resource::add, @@ -658,8 +656,8 @@ public ResourceTO getResourceTO(final ExternalResource resource) { resourceTO.setPropagationPolicy(resource.getPropagationPolicy() == null ? null : resource.getPropagationPolicy().getKey()); - resourceTO.setPullPolicy(resource.getPullPolicy() == null - ? null : resource.getPullPolicy().getKey()); + resourceTO.setInboundPolicy(resource.getInboundPolicy() == null + ? null : resource.getInboundPolicy().getKey()); resourceTO.setPushPolicy(resource.getPushPolicy() == null ? null : resource.getPushPolicy().getKey()); @@ -667,11 +665,9 @@ public ResourceTO getResourceTO(final ExternalResource resource) { resourceTO.setProvisionSorter(resource.getProvisionSorter() == null ? null : resource.getProvisionSorter().getKey()); - resourceTO.getConfOverride().addAll(resource.getConfOverride()); - Collections.sort(resourceTO.getConfOverride()); + resourceTO.setConfOverride(resource.getConfOverride()); - resourceTO.setOverrideCapabilities(resource.isOverrideCapabilities()); - resourceTO.getCapabilitiesOverride().addAll(resource.getCapabilitiesOverride()); + resourceTO.setCapabilitiesOverride(resource.getCapabilitiesOverride()); resourceTO.getPropagationActions().addAll( resource.getPropagationActions().stream().map(Implementation::getKey).toList()); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java index fd47dc728ad..713c6ee5766 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java @@ -34,6 +34,8 @@ import org.apache.syncope.common.lib.form.SyncopeForm; import org.apache.syncope.common.lib.to.ExecTO; import org.apache.syncope.common.lib.to.FormPropertyDefTO; +import org.apache.syncope.common.lib.to.InboundTaskTO; +import org.apache.syncope.common.lib.to.LiveSyncTaskTO; import org.apache.syncope.common.lib.to.MacroTaskTO; import org.apache.syncope.common.lib.to.NotificationTaskTO; import org.apache.syncope.common.lib.to.PropagationTaskTO; @@ -57,8 +59,11 @@ import org.apache.syncope.core.persistence.api.dao.TaskExecDAO; import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.persistence.api.entity.task.AnyTemplateLiveSyncTask; import org.apache.syncope.core.persistence.api.entity.task.AnyTemplatePullTask; import org.apache.syncope.core.persistence.api.entity.task.FormPropertyDef; +import org.apache.syncope.core.persistence.api.entity.task.InboundTask; +import org.apache.syncope.core.persistence.api.entity.task.LiveSyncTask; import org.apache.syncope.core.persistence.api.entity.task.MacroTask; import org.apache.syncope.core.persistence.api.entity.task.MacroTaskCommand; import org.apache.syncope.core.persistence.api.entity.task.NotificationTask; @@ -76,6 +81,7 @@ import org.apache.syncope.core.provisioning.api.macro.MacroActions; import org.apache.syncope.core.provisioning.java.job.MacroJobDelegate; import org.apache.syncope.core.provisioning.java.job.SyncopeTaskScheduler; +import org.apache.syncope.core.provisioning.java.pushpull.LiveSyncJobDelegate; import org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate; import org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate; import org.apache.syncope.core.provisioning.java.utils.TemplateUtils; @@ -128,7 +134,7 @@ public TaskDataBinderImpl( protected void fill(final ProvisioningTask provisioningTask, final ProvisioningTaskTO provisioningTaskTO) { if (provisioningTask instanceof final PushTask pushTask - && provisioningTaskTO instanceof final PushTaskTO pushTaskTO) { + && provisioningTaskTO instanceof final PushTaskTO pushTaskTO) { Implementation jobDelegate = pushTaskTO.getJobDelegate() == null ? implementationDAO.findByType(IdRepoImplementationType.TASKJOB_DELEGATE).stream(). @@ -159,62 +165,114 @@ protected void fill(final ProvisioningTask provisioningTask, final Provisioni // remove all filters not contained in the TO pushTask.getFilters().entrySet(). removeIf(filter -> !pushTaskTO.getFilters().containsKey(filter.getKey())); - } else if (provisioningTask instanceof final PullTask pullTask - && provisioningTaskTO instanceof final PullTaskTO pullTaskTO) { - - Implementation jobDelegate = pullTaskTO.getJobDelegate() == null - ? implementationDAO.findByType(IdRepoImplementationType.TASKJOB_DELEGATE).stream(). - filter(impl -> PullJobDelegate.class.getSimpleName().equals(impl.getKey())). - findFirst().orElse(null) - : implementationDAO.findById(pullTaskTO.getJobDelegate()).orElse(null); - if (jobDelegate == null) { - jobDelegate = entityFactory.newEntity(Implementation.class); - jobDelegate.setKey(PullJobDelegate.class.getSimpleName()); - jobDelegate.setEngine(ImplementationEngine.JAVA); - jobDelegate.setType(IdRepoImplementationType.TASKJOB_DELEGATE); - jobDelegate.setBody(PullJobDelegate.class.getName()); - jobDelegate = implementationDAO.save(jobDelegate); - } - pullTask.setJobDelegate(jobDelegate); + } else if (provisioningTask instanceof final InboundTask inboundTask + && provisioningTaskTO instanceof final InboundTaskTO inboundTaskTO) { + + inboundTask.setDestinationRealm(realmSearchDAO.findByFullPath(inboundTaskTO.getDestinationRealm()). + orElseThrow(() -> new NotFoundException("Realm " + inboundTaskTO.getDestinationRealm()))); + + inboundTask.setMatchingRule(inboundTaskTO.getMatchingRule() == null + ? MatchingRule.UPDATE : inboundTaskTO.getMatchingRule()); + inboundTask.setUnmatchingRule(inboundTaskTO.getUnmatchingRule() == null + ? UnmatchingRule.PROVISION : inboundTaskTO.getUnmatchingRule()); + + inboundTask.setRemediation(inboundTaskTO.isRemediation()); + + if (provisioningTask instanceof final LiveSyncTask liveSyncTask + && provisioningTaskTO instanceof final LiveSyncTaskTO liveSyncTaskTO) { + + Implementation jobDelegate = liveSyncTaskTO.getJobDelegate() == null + ? implementationDAO.findByType(IdRepoImplementationType.TASKJOB_DELEGATE).stream(). + filter(impl -> LiveSyncJobDelegate.class.getSimpleName().equals(impl.getKey())). + findFirst().orElse(null) + : implementationDAO.findById(liveSyncTaskTO.getJobDelegate()).orElse(null); + if (jobDelegate == null) { + jobDelegate = entityFactory.newEntity(Implementation.class); + jobDelegate.setKey(LiveSyncJobDelegate.class.getSimpleName()); + jobDelegate.setEngine(ImplementationEngine.JAVA); + jobDelegate.setType(IdRepoImplementationType.TASKJOB_DELEGATE); + jobDelegate.setBody(LiveSyncJobDelegate.class.getName()); + jobDelegate = implementationDAO.save(jobDelegate); + } + liveSyncTask.setJobDelegate(jobDelegate); + + if (liveSyncTaskTO.getLiveSyncDeltaMapper() == null) { + liveSyncTask.setLiveSyncDeltaMapper(null); + } else { + implementationDAO.findById(liveSyncTaskTO.getLiveSyncDeltaMapper()).ifPresentOrElse( + liveSyncTask::setLiveSyncDeltaMapper, + () -> LOG.debug("Invalid Implementation {}, ignoring...", + liveSyncTaskTO.getLiveSyncDeltaMapper())); + } - pullTask.setPullMode(pullTaskTO.getPullMode()); + // validate JEXL expressions from templates and proceed if fine + TemplateUtils.check(liveSyncTaskTO.getTemplates(), ClientExceptionType.InvalidLiveSyncTask); + liveSyncTaskTO.getTemplates().forEach((type, template) -> anyTypeDAO.findById(type).ifPresentOrElse( + anyType -> { + AnyTemplateLiveSyncTask anyTemplate = liveSyncTask.getTemplate(anyType.getKey()). + orElse(null); + if (anyTemplate == null) { + anyTemplate = entityFactory.newEntity(AnyTemplateLiveSyncTask.class); + anyTemplate.setAnyType(anyType); + anyTemplate.setLiveSyncTask(liveSyncTask); + + liveSyncTask.add(anyTemplate); + } + anyTemplate.set(template); + }, + () -> LOG.debug("Invalid AnyType {} specified, ignoring...", type))); + // remove all templates not contained in the TO + liveSyncTask.getTemplates().removeIf( + anyTemplate -> !liveSyncTaskTO.getTemplates().containsKey(anyTemplate.getAnyType().getKey())); + } else if (provisioningTask instanceof final PullTask pullTask + && provisioningTaskTO instanceof final PullTaskTO pullTaskTO) { + + Implementation jobDelegate = pullTaskTO.getJobDelegate() == null + ? implementationDAO.findByType(IdRepoImplementationType.TASKJOB_DELEGATE).stream(). + filter(impl -> PullJobDelegate.class.getSimpleName().equals(impl.getKey())). + findFirst().orElse(null) + : implementationDAO.findById(pullTaskTO.getJobDelegate()).orElse(null); + if (jobDelegate == null) { + jobDelegate = entityFactory.newEntity(Implementation.class); + jobDelegate.setKey(PullJobDelegate.class.getSimpleName()); + jobDelegate.setEngine(ImplementationEngine.JAVA); + jobDelegate.setType(IdRepoImplementationType.TASKJOB_DELEGATE); + jobDelegate.setBody(PullJobDelegate.class.getName()); + jobDelegate = implementationDAO.save(jobDelegate); + } + pullTask.setJobDelegate(jobDelegate); - if (pullTaskTO.getReconFilterBuilder() == null) { - pullTask.setReconFilterBuilder(null); - } else { - implementationDAO.findById(pullTaskTO.getReconFilterBuilder()).ifPresentOrElse( - pullTask::setReconFilterBuilder, - () -> LOG.debug("Invalid Implementation {}, ignoring...", pullTaskTO.getReconFilterBuilder())); - } + pullTask.setPullMode(pullTaskTO.getPullMode()); - pullTask.setDestinationRealm(realmSearchDAO.findByFullPath(pullTaskTO.getDestinationRealm()). - orElseThrow(() -> new NotFoundException("Realm " + pullTaskTO.getDestinationRealm()))); - - pullTask.setMatchingRule(pullTaskTO.getMatchingRule() == null - ? MatchingRule.UPDATE : pullTaskTO.getMatchingRule()); - pullTask.setUnmatchingRule(pullTaskTO.getUnmatchingRule() == null - ? UnmatchingRule.PROVISION : pullTaskTO.getUnmatchingRule()); - - // validate JEXL expressions from templates and proceed if fine - TemplateUtils.check(pullTaskTO.getTemplates(), ClientExceptionType.InvalidPullTask); - pullTaskTO.getTemplates().forEach((type, template) -> anyTypeDAO.findById(type).ifPresentOrElse( - anyType -> { - AnyTemplatePullTask anyTemplate = pullTask.getTemplate(anyType.getKey()).orElse(null); - if (anyTemplate == null) { - anyTemplate = entityFactory.newEntity(AnyTemplatePullTask.class); - anyTemplate.setAnyType(anyType); - anyTemplate.setPullTask(pullTask); - - pullTask.add(anyTemplate); - } - anyTemplate.set(template); - }, - () -> LOG.debug("Invalid AnyType {} specified, ignoring...", type))); - // remove all templates not contained in the TO - pullTask.getTemplates(). - removeIf(anyTemplate -> !pullTaskTO.getTemplates().containsKey(anyTemplate.getAnyType().getKey())); + if (pullTaskTO.getReconFilterBuilder() == null) { + pullTask.setReconFilterBuilder(null); + } else { + implementationDAO.findById(pullTaskTO.getReconFilterBuilder()).ifPresentOrElse( + pullTask::setReconFilterBuilder, + () -> LOG.debug("Invalid Implementation {}, ignoring...", + pullTaskTO.getReconFilterBuilder())); + } - pullTask.setRemediation(pullTaskTO.isRemediation()); + // validate JEXL expressions from templates and proceed if fine + TemplateUtils.check(pullTaskTO.getTemplates(), ClientExceptionType.InvalidPullTask); + pullTaskTO.getTemplates().forEach((type, template) -> anyTypeDAO.findById(type).ifPresentOrElse( + anyType -> { + AnyTemplatePullTask anyTemplate = pullTask.getTemplate(anyType.getKey()).orElse(null); + if (anyTemplate == null) { + anyTemplate = entityFactory.newEntity(AnyTemplatePullTask.class); + anyTemplate.setAnyType(anyType); + anyTemplate.setPullTask(pullTask); + + pullTask.add(anyTemplate); + } + anyTemplate.set(template); + }, + () -> LOG.debug("Invalid AnyType {} specified, ignoring...", type))); + // remove all templates not contained in the TO + pullTask.getTemplates(). + removeIf(anyTemplate -> !pullTaskTO.getTemplates(). + containsKey(anyTemplate.getAnyType().getKey())); + } } // 3. fill the remaining fields @@ -296,8 +354,8 @@ protected void fill(final MacroTask macroTask, final MacroTaskTO macroTaskTO) { @Override public SchedTask createSchedTask(final SchedTaskTO taskTO, final TaskUtils taskUtils) { - Class taskTOClass = taskUtils.taskTOClass(); - if (taskTOClass == null || !taskTOClass.equals(taskTO.getClass())) { + Class taskTOClass = taskUtils.getType().getToClass(); + if (!taskTOClass.equals(taskTO.getClass())) { throw new IllegalArgumentException(String.format("Expected %s, found %s", taskTOClass, taskTO.getClass())); } @@ -348,8 +406,8 @@ public SchedTask createSchedTask(final SchedTaskTO taskTO, final TaskUtils taskU @Override public void updateSchedTask(final SchedTask task, final SchedTaskTO taskTO, final TaskUtils taskUtils) { - Class taskTOClass = taskUtils.taskTOClass(); - if (taskTOClass == null || !taskTOClass.equals(taskTO.getClass())) { + Class taskTOClass = taskUtils.getType().getToClass(); + if (!taskTOClass.equals(taskTO.getClass())) { throw new IllegalArgumentException(String.format("Expected %s, found %s", taskTOClass, taskTO.getClass())); } @@ -364,10 +422,13 @@ public void updateSchedTask(final SchedTask task, final SchedTaskTO taskTO, fina task.setCronExpression(taskTO.getCronExpression()); task.setActive(taskTO.isActive()); - if (task instanceof MacroTask) { - fill((MacroTask) task, (MacroTaskTO) taskTO); - } else if (task instanceof ProvisioningTask) { - fill((ProvisioningTask) task, (ProvisioningTaskTO) taskTO); + switch (task) { + case MacroTask macroTask -> + fill(macroTask, (MacroTaskTO) taskTO); + case ProvisioningTask provisioningTask -> + fill(provisioningTask, (ProvisioningTaskTO) taskTO); + default -> { + } } } @@ -407,6 +468,7 @@ protected void fill(final SchedTaskTO schedTaskTO, final SchedTask schedTask) { schedTaskTO.setDescription(schedTask.getDescription()); schedTaskTO.setCronExpression(schedTask.getCronExpression()); schedTaskTO.setActive(schedTask.isActive()); + schedTaskTO.setJobDelegate(schedTask.getJobDelegate().getKey()); schedTaskTO.getExecutions().stream().sorted(Comparator.comparing(ExecTO::getStart).reversed()).findFirst(). map(ExecTO::getStart).ifPresentOrElse( @@ -417,7 +479,7 @@ protected void fill(final SchedTaskTO schedTaskTO, final SchedTask schedTask) { ifPresent(schedTaskTO::setNextExec); if (schedTaskTO instanceof final ProvisioningTaskTO provisioningTaskTO - && schedTask instanceof final ProvisioningTask provisioningTask) { + && schedTask instanceof final ProvisioningTask provisioningTask) { provisioningTaskTO.setResource(provisioningTask.getResource().getKey()); @@ -473,8 +535,6 @@ public T getTaskTO(final Task task, final TaskUtils taskUt SchedTask schedTask = (SchedTask) task; SchedTaskTO schedTaskTO = (SchedTaskTO) taskTO; - schedTaskTO.setJobDelegate(schedTask.getJobDelegate().getKey()); - fill(schedTaskTO, schedTask); } @@ -484,7 +544,6 @@ public T getTaskTO(final Task task, final TaskUtils taskUt fill(macroTaskTO, macroTask); - macroTaskTO.setJobDelegate(macroTask.getJobDelegate().getKey()); macroTaskTO.setRealm(macroTask.getRealm().getFullPath()); macroTask.getCommands().forEach(mct -> macroTaskTO.getCommands().add( @@ -515,6 +574,25 @@ public T getTaskTO(final Task task, final TaskUtils taskUt ifPresent(fv -> macroTaskTO.setMacroActions(fv.getKey())); } + case LIVE_SYNC -> { + LiveSyncTask liveSyncTask = (LiveSyncTask) task; + LiveSyncTaskTO liveSyncTaskTO = (LiveSyncTaskTO) taskTO; + + fill(liveSyncTaskTO, liveSyncTask); + + liveSyncTaskTO.setDestinationRealm(liveSyncTask.getDestinationRealm().getFullPath()); + liveSyncTaskTO.setMatchingRule(liveSyncTask.getMatchingRule() == null + ? MatchingRule.UPDATE : liveSyncTask.getMatchingRule()); + liveSyncTaskTO.setUnmatchingRule(liveSyncTask.getUnmatchingRule() == null + ? UnmatchingRule.PROVISION : liveSyncTask.getUnmatchingRule()); + + liveSyncTaskTO.setLiveSyncDeltaMapper(liveSyncTask.getLiveSyncDeltaMapper().getKey()); + + liveSyncTask.getTemplates(). + forEach(template -> liveSyncTaskTO.getTemplates(). + put(template.getAnyType().getKey(), template.get())); + } + case PULL -> { PullTask pullTask = (PullTask) task; PullTaskTO pullTaskTO = (PullTaskTO) taskTO; diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java index d77ac1d9f98..51744fd0a65 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AbstractSchedTaskJobDelegate.java @@ -89,18 +89,19 @@ public abstract class AbstractSchedTaskJobDelegate implemen @Autowired protected ApplicationEventPublisher publisher; + protected boolean manageOperationId; + + protected String executor; + protected void setStatus(final String status) { publisher.publishEvent(new JobStatusEvent( this, AuthContextUtils.getDomain(), JobNamer.getJobName(task), status)); } @SuppressWarnings("unchecked") - @Transactional - @Override - public void execute( + protected void init( final TaskType taskType, final String taskKey, - final boolean dryRun, final JobExecutionContext context) throws JobExecutionException { @@ -113,35 +114,33 @@ public void execute( return; } - boolean manageOperationId = Optional.ofNullable(MDC.get(Job.OPERATION_ID)). + manageOperationId = Optional.ofNullable(MDC.get(Job.OPERATION_ID)). map(operationId -> false). orElseGet(() -> { MDC.put(Job.OPERATION_ID, SecureRandomUtils.generateRandomUUID().toString()); return true; }); - String executor = Optional.ofNullable(context.getExecutor()).orElse(securityProperties.getAdminUser()); + executor = Optional.ofNullable(context.getExecutor()).orElse(securityProperties.getAdminUser()); + } + + protected TaskExec initExecution() { TaskExec execution = taskUtilsFactory.getInstance(taskType).newTaskExec(); execution.setStart(OffsetDateTime.now()); execution.setTask(task); execution.setExecutor(executor); - setStatus("Initialization completed"); - - OpEvent.Outcome result; - - try { - execution.setMessage(doExecute(dryRun, executor, context)); - execution.setStatus(TaskJob.Status.SUCCESS.name()); + return execution; + } - result = OpEvent.Outcome.SUCCESS; - } catch (JobExecutionException e) { - LOG.error("While executing task {}", taskKey, e); - result = OpEvent.Outcome.FAILURE; + protected void endExecution( + final TaskExec execution, + final String message, + final String status, + final OpEvent.Outcome result) { - execution.setMessage(ExceptionUtils2.getFullStackTrace(e)); - execution.setStatus(TaskJob.Status.FAILURE.name()); - } + execution.setMessage(message); + execution.setStatus(status); execution.setEnd(OffsetDateTime.now()); if (hasToBeRegistered(execution)) { @@ -169,23 +168,56 @@ public void execute( result, task, execution); + } + protected void end() { if (manageOperationId) { MDC.remove(Job.OPERATION_ID); } } + @SuppressWarnings("unchecked") + @Transactional + @Override + public void execute( + final TaskType taskType, + final String taskKey, + final JobExecutionContext context) + throws JobExecutionException { + + init(taskType, taskKey, context); + + setStatus("Initialization completed"); + + TaskExec execution = initExecution(); + + String message; + String status; + OpEvent.Outcome result; + try { + message = doExecute(context); + status = TaskJob.Status.SUCCESS.name(); + result = OpEvent.Outcome.SUCCESS; + } catch (JobExecutionException e) { + LOG.error("While executing task {}", taskKey, e); + + message = ExceptionUtils2.getFullStackTrace(e); + status = TaskJob.Status.FAILURE.name(); + result = OpEvent.Outcome.FAILURE; + } + endExecution(execution, message, status, result); + + end(); + } + /** * The actual execution, delegated to child classes. * - * @param dryRun whether to actually touch the data - * @param executor the user executing this task * @param context job execution context, can be used to pass parameters to the job * @return the task execution status to be set * @throws JobExecutionException if anything goes wrong */ - protected abstract String doExecute(boolean dryRun, String executor, JobExecutionContext context) - throws JobExecutionException; + protected abstract String doExecute(JobExecutionContext context) throws JobExecutionException; /** * Template method to determine whether this job's task execution has to be persisted or not. diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AfterHandlingJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AfterHandlingJob.java index afe24e202a0..cf9be0d4cf5 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AfterHandlingJob.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/AfterHandlingJob.java @@ -76,12 +76,10 @@ protected void execute(final JobExecutionContext context) throws JobExecutionExc } try { - AuthContextUtils.runAsAdmin( - context.getDomain(), - () -> { - notificationManager.createTasks(event.get()); - auditManager.audit(event.get()); - }); + AuthContextUtils.runAsAdmin(context.getDomain(), () -> { + notificationManager.createTasks(event.get()); + auditManager.audit(event.get()); + }); } catch (RuntimeException e) { throw new JobExecutionException("While handling notification / audit events", e); } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredAccessTokenCleanup.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredAccessTokenCleanup.java index 09e4e799f69..b93a508be79 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredAccessTokenCleanup.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ExpiredAccessTokenCleanup.java @@ -30,9 +30,8 @@ public class ExpiredAccessTokenCleanup extends AbstractSchedTaskJobDelegate private BatchDAO batchDAO; @Override - protected String doExecute(final boolean dryRun, final String executor, final JobExecutionContext context) { - - if (!dryRun) { + protected String doExecute(final JobExecutionContext context) { + if (!context.isDryRun()) { long deleted = batchDAO.deleteExpired(); LOG.debug("Successfully deleted {} expired batch requests", deleted); } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/GroupMemberProvisionTaskJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/GroupMemberProvisionTaskJobDelegate.java index 31aad3a9c0f..6db78bcc0d7 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/GroupMemberProvisionTaskJobDelegate.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/GroupMemberProvisionTaskJobDelegate.java @@ -68,19 +68,17 @@ public class GroupMemberProvisionTaskJobDelegate extends AbstractSchedTaskJobDel public void execute( final TaskType taskType, final String taskKey, - final boolean dryRun, final JobExecutionContext context) throws JobExecutionException { groupKey = (String) context.getData().get(GROUP_KEY_JOBDETAIL_KEY); action = (ProvisionAction) context.getData().get(ACTION_JOBDETAIL_KEY); - super.execute(taskType, taskKey, dryRun, context); + super.execute(taskType, taskKey, context); } @Override - protected String doExecute(final boolean dryRun, final String executor, final JobExecutionContext context) { - + protected String doExecute(final JobExecutionContext context) { Group group = groupDAO.authFind(groupKey); StringBuilder result = new StringBuilder("Group ").append(group.getName()).append(" members "); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/LiveSyncJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/LiveSyncJob.java new file mode 100644 index 00000000000..9f51e0a4a2f --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/LiveSyncJob.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.provisioning.java.job; + +import org.apache.syncope.common.lib.types.TaskType; +import org.apache.syncope.core.persistence.api.entity.Implementation; +import org.apache.syncope.core.provisioning.api.job.JobExecutionContext; +import org.apache.syncope.core.provisioning.api.job.JobExecutionException; +import org.apache.syncope.core.provisioning.api.job.JobManager; +import org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate; +import org.apache.syncope.core.provisioning.java.pushpull.LiveSyncJobDelegate; +import org.apache.syncope.core.spring.implementation.ImplementationManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LiveSyncJob extends TaskJob { + + private static final Logger LOG = LoggerFactory.getLogger(LiveSyncJob.class); + + private LiveSyncJobDelegate delegate; + + @Override + public SchedTaskJobDelegate getDelegate() { + return delegate; + } + + @Override + protected void delegate(final JobExecutionContext context, final String taskKey) + throws ClassNotFoundException, JobExecutionException { + + String implKey = (String) context.getData().get(JobManager.DELEGATE_IMPLEMENTATION); + Implementation impl = implementationDAO.findById(implKey).orElse(null); + if (impl == null) { + LOG.error("Could not find Implementation '{}', aborting", implKey); + } else { + delegate = ImplementationManager.build(impl); + delegate.execute( + TaskType.LIVE_SYNC, + taskKey, + context); + } + } +} diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/MacroJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/MacroJobDelegate.java index c013ce12059..27d2397ee12 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/MacroJobDelegate.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/MacroJobDelegate.java @@ -73,7 +73,7 @@ public class MacroJobDelegate extends AbstractSchedTaskJobDelegate { protected Validator validator; @Resource(name = "batchExecutor") - protected VirtualThreadPoolTaskExecutor executor; + protected VirtualThreadPoolTaskExecutor taskExecutor; protected final Map perContextActions = new ConcurrentHashMap<>(); @@ -188,7 +188,7 @@ protected String run( final boolean dryRun) throws JobExecutionException { - Future>> future = executor.submit( + Future>> future = taskExecutor.submit( new DelegatingSecurityContextCallable<>(() -> { AtomicReference> error = new AtomicReference<>(); @@ -242,9 +242,7 @@ protected String run( @SuppressWarnings("unchecked") @Override - protected String doExecute(final boolean dryRun, final String executor, final JobExecutionContext context) - throws JobExecutionException { - + protected String doExecute(final JobExecutionContext context) throws JobExecutionException { Optional actions; if (task.getMacroActions() == null) { actions = Optional.empty(); @@ -264,7 +262,7 @@ protected String doExecute(final boolean dryRun, final String executor, final Jo SyncopeForm macroTaskForm = (SyncopeForm) context.getData().get(MACRO_TASK_FORM_JOBDETAIL_KEY); Optional jexlContext = check(macroTaskForm, actions, output); - if (!dryRun) { + if (!context.isDryRun()) { actions.ifPresent(MacroActions::beforeAll); } @@ -318,7 +316,7 @@ protected String doExecute(final boolean dryRun, final String executor, final Jo commands.add(Pair.of(runnable, args)); } - return run(commands, actions, output, dryRun); + return run(commands, actions, output, context.isDryRun()); } @Override diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SyncopeTaskScheduler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SyncopeTaskScheduler.java index d9ca2a048a4..c659c80c70c 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SyncopeTaskScheduler.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SyncopeTaskScheduler.java @@ -28,6 +28,7 @@ import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.core.persistence.api.dao.JobStatusDAO; +import org.apache.syncope.core.provisioning.api.job.StoppableSchedTaskJobDelegate; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,7 +44,7 @@ public class SyncopeTaskScheduler { protected final JobStatusDAO jobStatusDAO; - protected final Map, Pair>> tasks = new ConcurrentHashMap<>(); + protected final Map, Pair>> jobs = new ConcurrentHashMap<>(); public SyncopeTaskScheduler(final TaskScheduler scheduler, final JobStatusDAO jobStatusDAO) { this.scheduler = scheduler; @@ -51,58 +52,73 @@ public SyncopeTaskScheduler(final TaskScheduler scheduler, final JobStatusDAO jo } public void register(final Job job) { - tasks.put( + jobs.put( Pair.of(job.getContext().getDomain(), job.getContext().getJobName()), Pair.of(job, null)); } public void start(final String domain, final String jobName) { - Optional.ofNullable(tasks.get(Pair.of(domain, jobName))). + Optional.ofNullable(jobs.get(Pair.of(domain, jobName))). ifPresent(pair -> schedule(pair.getLeft(), Instant.now())); } + public void start(final Job job) { + schedule(job, Instant.now()); + } + public void schedule(final Job job, final CronTrigger trigger) { ScheduledFuture future = scheduler.schedule(job, trigger); - tasks.put( + jobs.put( Pair.of(job.getContext().getDomain(), job.getContext().getJobName()), Pair.of(job, future)); } public void schedule(final Job job, final Instant startTime) { ScheduledFuture future = scheduler.schedule(job, startTime); - tasks.put( + jobs.put( Pair.of(job.getContext().getDomain(), job.getContext().getJobName()), Pair.of(job, future)); } public boolean contains(final String domain, final String jobName) { - return tasks.containsKey(Pair.of(domain, jobName)); + return jobs.containsKey(Pair.of(domain, jobName)); } public Optional> getJobClass(final String domain, final String jobName) { - return Optional.ofNullable(tasks.get(Pair.of(domain, jobName))). + return Optional.ofNullable(jobs.get(Pair.of(domain, jobName))). map(pair -> AopUtils.getTargetClass(pair.getLeft())); } public Optional getNextTrigger(final String domain, final String jobName) { - return Optional.ofNullable(tasks.get(Pair.of(domain, jobName))). - filter(f -> f.getRight() != null). - map(f -> f.getRight().getDelay(TimeUnit.SECONDS)). + return Optional.ofNullable(jobs.get(Pair.of(domain, jobName))). + filter(pair -> pair.getRight() != null). + map(pair -> pair.getRight().getDelay(TimeUnit.SECONDS)). filter(delay -> delay > 0). map(delay -> OffsetDateTime.now().plusSeconds(delay)); } public void cancel(final String domain, final String jobName) { - Optional.ofNullable(tasks.get(Pair.of(domain, jobName))). - filter(f -> f.getRight() != null).ifPresent(f -> f.getRight().cancel(true)); + Optional.ofNullable(jobs.get(Pair.of(domain, jobName))).ifPresent(pair -> { + boolean mayInterruptIfRunning = true; + if (pair.getLeft() instanceof TaskJob taskJob + && taskJob.getDelegate() instanceof StoppableSchedTaskJobDelegate stoppable) { + + stoppable.stop(); + mayInterruptIfRunning = false; + } + + if (pair.getRight() != null) { + pair.getRight().cancel(mayInterruptIfRunning); + } + }); } public void delete(final String domain, final String jobName) { - tasks.remove(Pair.of(domain, jobName)); + jobs.remove(Pair.of(domain, jobName)); AuthContextUtils.runAsAdmin(domain, () -> jobStatusDAO.unlock(jobName)); } public List getJobNames(final String domain) { - return tasks.keySet().stream().filter(pair -> domain.equals(pair.getLeft())).map(Pair::getRight).toList(); + return jobs.keySet().stream().filter(pair -> domain.equals(pair.getLeft())).map(Pair::getRight).toList(); } } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java index 3acb31d25cf..fc281373c3b 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/TaskJob.java @@ -47,10 +47,32 @@ public enum Status { } @Autowired - private ImplementationDAO implementationDAO; + private DomainHolder domainHolder; @Autowired - private DomainHolder domainHolder; + protected ImplementationDAO implementationDAO; + + private SchedTaskJobDelegate delegate; + + public SchedTaskJobDelegate getDelegate() { + return delegate; + } + + protected void delegate(final JobExecutionContext context, final String taskKey) + throws ClassNotFoundException, JobExecutionException { + + String implKey = (String) context.getData().get(JobManager.DELEGATE_IMPLEMENTATION); + Implementation impl = implementationDAO.findById(implKey).orElse(null); + if (impl == null) { + LOG.error("Could not find Implementation '{}', aborting", implKey); + } else { + delegate = ImplementationManager.build(impl); + delegate.execute( + (TaskType) context.getData().get(JobManager.TASK_TYPE), + taskKey, + context); + } + } @Override protected void execute(final JobExecutionContext context) throws JobExecutionException { @@ -63,25 +85,19 @@ protected void execute(final JobExecutionContext context) throws JobExecutionExc try { AuthContextUtils.runAsAdmin(context.getDomain(), () -> { try { - String implKey = (String) context.getData().get(JobManager.DELEGATE_IMPLEMENTATION); - Implementation impl = implementationDAO.findById(implKey).orElse(null); - if (impl == null) { - LOG.error("Could not find Implementation '{}', aborting", implKey); - } else { - SchedTaskJobDelegate delegate = ImplementationManager.build(impl); - delegate.execute( - (TaskType) context.getData().get(JobManager.TASK_TYPE), - taskKey, - context.isDryRun(), - context); - } + delegate(context, taskKey); } catch (Exception e) { - LOG.error("While executing task {}", taskKey, e); + if (e instanceof RuntimeException re) { + throw re; + } throw new RuntimeException(e); } }); } catch (RuntimeException e) { LOG.error("While executing task {}", taskKey, e); + if (e.getCause() instanceof JobExecutionException jee) { + throw jee; + } throw new JobExecutionException("While executing task " + taskKey, e); } } 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 ab6ac761705..80fb5334916 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 @@ -779,17 +779,19 @@ protected Optional hasToBeregistered( ExternalResource resource = taskInfo.getResource(); boolean result; - result = - switch (taskInfo.getOperation()) { + result = switch (taskInfo.getOperation()) { case CREATE -> - (failed && resource.getCreateTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()) - || resource.getCreateTraceLevel() == TraceLevel.ALL; + resource.getCreateTraceLevel() == TraceLevel.ALL + || (failed && resource.getCreateTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()); + case UPDATE -> - (failed && resource.getUpdateTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()) - || resource.getUpdateTraceLevel() == TraceLevel.ALL; + resource.getUpdateTraceLevel() == TraceLevel.ALL + || (failed && resource.getUpdateTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()); + case DELETE -> - (failed && resource.getDeleteTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()) - || resource.getDeleteTraceLevel() == TraceLevel.ALL; + resource.getDeleteTraceLevel() == TraceLevel.ALL + || (failed && resource.getDeleteTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()); + default -> false; }; 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 8c64619fb93..5e5f7d65f2e 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 @@ -18,21 +18,21 @@ */ package org.apache.syncope.core.provisioning.java.pushpull; -import java.lang.reflect.ParameterizedType; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Optional; import org.apache.commons.lang3.StringUtils; -import org.apache.syncope.common.lib.to.Mapping; import org.apache.syncope.common.lib.to.Provision; import org.apache.syncope.common.lib.to.ProvisioningReport; import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.TaskType; import org.apache.syncope.common.lib.types.TraceLevel; import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; import org.apache.syncope.core.persistence.api.dao.PolicyDAO; +import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask; @@ -56,35 +56,63 @@ public abstract class AbstractProvisioningJobDelegate perContextProvisionSorter = Optional.empty(); + protected Connector connector; + + @Override + protected void init( + final TaskType taskType, + final String taskKey, + final JobExecutionContext context) throws JobExecutionException { + + super.init(taskType, taskKey, context); + + boolean noMapping = true; + for (Provision provision : task.getResource().getProvisions().stream(). + filter(provision -> provision.getMapping() != null).toList()) { + + noMapping = false; + if (provision.getMapping().getConnObjectKeyItem() == null) { + throw new JobExecutionException("Invalid ConnObjectKey mapping for provision " + provision); + } + } + if (noMapping) { + noMapping = task.getResource().getOrgUnit() == null; + } + if (noMapping) { + throw new JobExecutionException("No provisions nor orgUnit available: aborting..."); + } + + connector = connectorManager.getConnector(task.getResource()); + } + + @Override + protected boolean hasToBeRegistered(final TaskExec execution) { + // True if either failed and failures have to be registered, or if ALL has to be registered. + return (TaskJob.Status.valueOf(execution.getStatus()) == TaskJob.Status.FAILURE + && task.getResource().getProvisioningTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()) + || task.getResource().getProvisioningTraceLevel().ordinal() >= TraceLevel.SUMMARY.ordinal(); + } + protected ProvisionSorter getProvisionSorter(final T task) { if (task.getResource().getProvisionSorter() != null) { try { @@ -630,72 +658,4 @@ protected String createReport( return report.toString(); } - - protected Connector getConnector(final T provisioningTask) throws JobExecutionException { - Connector connector; - try { - connector = connectorManager.getConnector(provisioningTask.getResource()); - } catch (Exception e) { - String msg = String.format("Connector instance bean for resource %s and connInstance %s not found", - provisioningTask.getResource(), provisioningTask.getResource().getConnector()); - throw new JobExecutionException(msg, e); - } - - return connector; - } - - @Override - protected String doExecute(final boolean dryRun, final String executor, final JobExecutionContext context) - throws JobExecutionException { - - try { - Class clazz = getTaskClassReference(); - if (!clazz.isAssignableFrom(task.getClass())) { - throw new JobExecutionException("Task " + task.getKey() + " isn't a ProvisioningTask"); - } - - T provisioningTask = clazz.cast(task); - - Connector connector = getConnector(provisioningTask); - - boolean noMapping = true; - for (Provision provision : provisioningTask.getResource().getProvisions()) { - Mapping mapping = provision.getMapping(); - if (mapping != null) { - noMapping = false; - if (mapping.getConnObjectKeyItem() == null) { - throw new JobExecutionException("Invalid ConnObjectKey mapping for provision " + provision); - } - } - } - if (noMapping) { - noMapping = provisioningTask.getResource().getOrgUnit() == null; - } - if (noMapping) { - return "No provisions nor orgUnit available: aborting..."; - } - - return doExecuteProvisioning(provisioningTask, connector, dryRun, executor, context); - } catch (Throwable t) { - LOG.error("While executing provisioning job {}", getClass().getName(), t); - throw t; - } - } - - protected abstract String doExecuteProvisioning( - T task, Connector connector, boolean dryRun, String executor, JobExecutionContext context) - throws JobExecutionException; - - @Override - protected boolean hasToBeRegistered(final TaskExec execution) { - // True if either failed and failures have to be registered, or if ALL has to be registered. - return (TaskJob.Status.valueOf(execution.getStatus()) == TaskJob.Status.FAILURE - && task.getResource().getProvisioningTraceLevel().ordinal() >= TraceLevel.FAILURES.ordinal()) - || task.getResource().getProvisioningTraceLevel().ordinal() >= TraceLevel.SUMMARY.ordinal(); - } - - @SuppressWarnings("unchecked") - protected Class getTaskClassReference() { - return (Class) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]; - } } 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 390c6eed8d6..a3887b05bbd 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 @@ -56,9 +56,9 @@ import org.apache.syncope.core.provisioning.api.notification.NotificationManager; import org.apache.syncope.core.provisioning.api.propagation.PropagationException; import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException; -import org.apache.syncope.core.provisioning.api.pushpull.PullActions; +import org.apache.syncope.core.provisioning.api.pushpull.InboundActions; import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullResultHandler; -import org.apache.syncope.core.provisioning.api.rules.PullMatch; +import org.apache.syncope.core.provisioning.api.rules.InboundMatch; import org.apache.syncope.core.provisioning.java.cache.VirAttrCacheKey; import org.apache.syncope.core.provisioning.java.cache.VirAttrCacheValue; import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils; @@ -71,7 +71,7 @@ import org.springframework.transaction.annotation.Transactional; public abstract class AbstractPullResultHandler - extends AbstractSyncopeResultHandler + extends AbstractSyncopeResultHandler implements SyncopePullResultHandler { protected static OpEvent.Outcome and(final OpEvent.Outcome left, final OpEvent.Outcome right) { @@ -144,6 +144,11 @@ public boolean handle(final SyncDelta delta) { LOG.debug("Successfully handled {}", delta); + if (stopRequested) { + LOG.debug("Stop was requested"); + return false; + } + if (profile.getTask().getPullMode() != PullMode.INCREMENTAL) { return true; } @@ -177,7 +182,7 @@ protected void throwIgnoreProvisionException(final SyncDelta delta, final Except } IgnoreProvisionException ipe = null; - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { if (ipe == null) { ipe = action.onError(profile, delta, exception); } @@ -225,7 +230,7 @@ protected OpEvent.Outcome provision( Object output; OpEvent.Outcome resultStatus; try { - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { if (rule == UnmatchingRule.ASSIGN) { action.beforeAssign(profile, delta, anyCR); } else if (rule == UnmatchingRule.PROVISION) { @@ -240,7 +245,7 @@ protected OpEvent.Outcome provision( result.setName(getName(created)); resultStatus = OpEvent.Outcome.SUCCESS; - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.after(profile, delta, created, result); } @@ -276,7 +281,7 @@ protected OpEvent.Outcome provision( protected OpEvent.Outcome update( final SyncDelta delta, - final List matches, + final List matches, final Provision provision) throws JobExecutionException { if (!profile.getTask().isPerformUpdate()) { @@ -289,7 +294,7 @@ protected OpEvent.Outcome update( LOG.debug("About to update {}", matches); OpEvent.Outcome global = OpEvent.Outcome.SUCCESS; - for (PullMatch match : matches) { + for (InboundMatch match : matches) { LOG.debug("About to update {}", match); ProvisioningReport result = new ProvisioningReport(); @@ -326,14 +331,14 @@ protected OpEvent.Outcome update( match.getAny().getType().getKind(), provision); - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.beforeUpdate(profile, delta, before, anyUR); } effectiveReq = doUpdate(before, anyUR, delta, result); AnyTO updated = AnyOperations.patch(before, effectiveReq); - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.after(profile, delta, updated, result); } @@ -382,7 +387,7 @@ protected OpEvent.Outcome update( protected OpEvent.Outcome deprovision( final MatchingRule matchingRule, final SyncDelta delta, - final List matches, + final List matches, final Provision provision) throws JobExecutionException { @@ -396,7 +401,7 @@ protected OpEvent.Outcome deprovision( LOG.debug("About to deprovision {}", matches); OpEvent.Outcome global = OpEvent.Outcome.SUCCESS; - for (PullMatch match : matches) { + for (InboundMatch match : matches) { LOG.debug("About to unassign resource {}", match); ProvisioningReport result = new ProvisioningReport(); @@ -425,11 +430,11 @@ protected OpEvent.Outcome deprovision( try { if (matchingRule == MatchingRule.UNASSIGN) { - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.beforeUnassign(profile, delta, before); } } else if (matchingRule == MatchingRule.DEPROVISION) { - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.beforeDeprovision(profile, delta, before); } } @@ -459,7 +464,7 @@ protected OpEvent.Outcome deprovision( output = doUpdate(before, anyUR, delta, result); } - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.after(profile, delta, AnyTO.class.cast(output), result); } @@ -498,7 +503,7 @@ protected OpEvent.Outcome deprovision( protected OpEvent.Outcome link( final SyncDelta delta, - final List matches, + final List matches, final Provision provision, final boolean unlink) throws JobExecutionException { @@ -516,7 +521,7 @@ protected OpEvent.Outcome link( LOG.debug("About to update {}", matches); OpEvent.Outcome global = OpEvent.Outcome.SUCCESS; - for (PullMatch match : matches) { + for (InboundMatch match : matches) { LOG.debug("About to unassign resource {}", match); ProvisioningReport result = new ProvisioningReport(); @@ -546,11 +551,11 @@ protected OpEvent.Outcome link( try { if (unlink) { - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.beforeUnlink(profile, delta, before); } } else { - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.beforeLink(profile, delta, before); } } @@ -563,7 +568,7 @@ protected OpEvent.Outcome link( effectiveReq = update(anyUR).getResult(); output = AnyOperations.patch(before, effectiveReq); - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.after(profile, delta, AnyTO.class.cast(output), result); } @@ -604,7 +609,7 @@ protected OpEvent.Outcome link( protected OpEvent.Outcome delete( final SyncDelta delta, - final List matches, + final List matches, final Provision provision) { if (!profile.getTask().isPerformDelete()) { @@ -617,7 +622,7 @@ protected OpEvent.Outcome delete( LOG.debug("About to delete {}", matches); OpEvent.Outcome global = OpEvent.Outcome.SUCCESS; - for (PullMatch match : matches) { + for (InboundMatch match : matches) { Object output; OpEvent.Outcome resultStatus = OpEvent.Outcome.FAILURE; @@ -634,7 +639,7 @@ protected OpEvent.Outcome delete( result.setUidValue(delta.getUid().getUidValue()); if (!profile.isDryRun()) { - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.beforeDelete(profile, delta, before); } @@ -644,11 +649,11 @@ protected OpEvent.Outcome delete( Set.of(profile.getTask().getResource().getKey()), true, profile.getExecutor(), - getContext()); + profile.getContext()); output = null; resultStatus = OpEvent.Outcome.SUCCESS; - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.after(profile, delta, before, result); } } catch (Exception e) { @@ -688,7 +693,7 @@ protected OpEvent.Outcome delete( protected OpEvent.Outcome ignore( final SyncDelta delta, - final List matches, + final List matches, final Provision provision, final boolean matching, final String... message) { @@ -736,7 +741,7 @@ protected OpEvent.Outcome ignore( protected OpEvent.Outcome handleAnys( final SyncDelta delta, - final List matches, + final List matches, final AnyTypeKind anyTypeKind, final Provision provision) throws JobExecutionException { @@ -811,7 +816,7 @@ protected OpEvent.Outcome handleAnys( break; case DELETE: - // Skip DELETE in case of PullCorrelationRule.NO_MATCH + // Skip DELETE in case of InboundCorrelationRule.NO_MATCH result = matches.get(0).getAny() == null ? OpEvent.Outcome.SUCCESS : delete(delta, matches, provision); break; @@ -823,7 +828,7 @@ protected OpEvent.Outcome handleAnys( protected OpEvent.Outcome handleLinkedAccounts( final SyncDelta delta, - final List matches, + final List matches, final Provision provision) throws JobExecutionException { if (matches.isEmpty()) { @@ -854,7 +859,7 @@ protected OpEvent.Outcome doHandle( delta.getDeltaType(), delta.getUid().getUidValue(), delta.getObject().getObjectClass()); SyncDelta finalDelta = delta; - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { finalDelta = action.preprocess(profile, finalDelta); } @@ -863,7 +868,7 @@ protected OpEvent.Outcome doHandle( OpEvent.Outcome result = OpEvent.Outcome.SUCCESS; try { - List matches = inboundMatcher.match( + List matches = inboundMatcher.match( finalDelta, profile.getTask().getResource(), provision, 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 d1ed130f1b8..57d30d0175a 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 @@ -261,6 +261,12 @@ public boolean handle(final String anyKey) { } doHandle(any, provision); + + if (stopRequested) { + LOG.debug("Stop was requested"); + return false; + } + return true; } catch (IgnoreProvisionException e) { ProvisioningReport ignoreResult = profile.getResults().stream(). @@ -357,8 +363,8 @@ protected void doHandle(final Any any, final Provision provision) throws JobE Object output = null; OpEvent.Outcome resultStatus = null; - Boolean enable = any instanceof User && profile.getTask().isSyncStatus() - ? BooleanUtils.negate(((User) any).isSuspended()) + Boolean enable = any instanceof User user && profile.getTask().isSyncStatus() + ? BooleanUtils.negate(user.isSuspended()) : null; try { if (beforeObj == null) { diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java index d1b45aef5ae..a12650c219a 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractRealmResultHandler.java @@ -80,8 +80,15 @@ public abstract class AbstractRealmResultHandler, */ protected ProvisioningProfile profile; + protected volatile boolean stopRequested = false; + @Override public void setProfile(final ProvisioningProfile profile) { this.profile = profile; } + + @Override + public void stop() { + stopRequested = true; + } } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java index 14c7e27f44e..ca35ef3c472 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java @@ -93,6 +93,8 @@ public abstract class AbstractSyncopeResultHandler */ protected ProvisioningProfile profile; + protected volatile boolean stopRequested = false; + protected abstract AnyUtils getAnyUtils(); protected abstract AnyTO getAnyTO(Any any); @@ -104,8 +106,8 @@ public void setProfile(final ProvisioningProfile profile) { this.profile = profile; } - protected String getContext() { - return (getClass().getSimpleName().contains("Pull") ? "PullTask" : "PushTask") - + " " + profile.getTask().getKey() + " '" + profile.getTask().getName() + "'"; + @Override + public void stop() { + stopRequested = true; } } 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 9d020b8faee..45557c2a7a8 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 @@ -31,20 +31,20 @@ import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.entity.ConnInstance; import org.apache.syncope.core.provisioning.api.Connector; +import org.apache.syncope.core.provisioning.api.pushpull.InboundActions; import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile; -import org.apache.syncope.core.provisioning.api.pushpull.PullActions; -import org.identityconnectors.framework.common.objects.SyncDelta; +import org.identityconnectors.framework.common.objects.LiveSyncDelta; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; /** - * A {@link org.apache.syncope.core.provisioning.api.pushpull.PullActions} implementation which allows the ability to + * A {@link org.apache.syncope.core.provisioning.api.pushpull.InboundActions} implementation which allows the ability to * import passwords from a Database backend, where the passwords are hashed according to the password cipher algorithm * property of the (DB) Connector and HEX-encoded. */ -public class DBPasswordPullActions implements PullActions { +public class DBPasswordPullActions implements InboundActions { protected static final Logger LOG = LoggerFactory.getLogger(DBPasswordPullActions.class); @@ -61,7 +61,7 @@ public class DBPasswordPullActions implements PullActions { @Override public void beforeProvision( final ProvisioningProfile profile, - final SyncDelta delta, + final LiveSyncDelta delta, final AnyCR anyCR) { if (anyCR instanceof UserCR userCR) { @@ -73,7 +73,7 @@ public void beforeProvision( @Override public void beforeUpdate( final ProvisioningProfile profile, - final SyncDelta delta, + final LiveSyncDelta delta, final EntityTO entityTO, final AnyUR anyUR) { @@ -112,7 +112,7 @@ protected String getCipherAlgorithm(final ConnInstance connInstance) { @Override public void after( final ProvisioningProfile profile, - final SyncDelta delta, + final LiveSyncDelta delta, final EntityTO any, final ProvisioningReport result) { 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 9de0422d0e0..3c08f75bdd0 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 @@ -73,7 +73,7 @@ protected AnyTO getAnyTO(final Any any) { @Override protected WorkflowResult update(final AnyUR req) { - return awfAdapter.update((AnyObjectUR) req, profile.getExecutor(), getContext()); + return awfAdapter.update((AnyObjectUR) req, profile.getExecutor(), profile.getContext()); } @Override @@ -85,7 +85,7 @@ protected AnyTO doCreate(final AnyCR anyCR, final SyncDelta delta) { Set.of(profile.getTask().getResource().getKey()), true, profile.getExecutor(), - getContext()); + profile.getContext()); return anyObjectDataBinder.getAnyObjectTO(created.getKey()); } @@ -104,7 +104,7 @@ protected AnyUR doUpdate( Set.of(profile.getTask().getResource().getKey()), true, profile.getExecutor(), - getContext()); + profile.getContext()); createRemediationIfNeeded(req, delta, result); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultAnyObjectPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultAnyObjectPushResultHandler.java index 2cf00226915..3fc635f91d1 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultAnyObjectPushResultHandler.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultAnyObjectPushResultHandler.java @@ -47,6 +47,6 @@ protected AnyTO getAnyTO(final Any any) { @Override protected WorkflowResult update(final AnyUR req) { - return awfAdapter.update((AnyObjectUR) req, profile.getExecutor(), getContext()); + return awfAdapter.update((AnyObjectUR) req, profile.getExecutor(), profile.getContext()); } } 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 ecda30e06cc..2d1a912fdb5 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 @@ -83,7 +83,7 @@ protected AnyTO getAnyTO(final Any any) { @Override protected WorkflowResult update(final AnyUR req) { - return gwfAdapter.update((GroupUR) req, profile.getExecutor(), getContext()); + return gwfAdapter.update((GroupUR) req, profile.getExecutor(), profile.getContext()); } @Override @@ -96,7 +96,7 @@ protected AnyTO doCreate(final AnyCR anyCR, final SyncDelta delta) { Set.of(profile.getTask().getResource().getKey()), true, profile.getExecutor(), - getContext()); + profile.getContext()); return groupDataBinder.getGroupTO(created.getKey()); } @@ -115,7 +115,7 @@ protected AnyUR doUpdate( Set.of(profile.getTask().getResource().getKey()), true, profile.getExecutor(), - getContext()); + profile.getContext()); createRemediationIfNeeded(req, delta, result); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultGroupPushResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultGroupPushResultHandler.java index bf2151350ff..b00adfab4bf 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultGroupPushResultHandler.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultGroupPushResultHandler.java @@ -47,6 +47,6 @@ protected AnyTO getAnyTO(final Any any) { @Override protected WorkflowResult update(final AnyUR req) { - return gwfAdapter.update((GroupUR) req, profile.getExecutor(), getContext()); + return gwfAdapter.update((GroupUR) req, profile.getExecutor(), profile.getContext()); } } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultPullCorrelationRule.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultInboundCorrelationRule.java similarity index 76% rename from core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultPullCorrelationRule.java rename to core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultInboundCorrelationRule.java index ad42114008a..9d06215d4be 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultPullCorrelationRule.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultInboundCorrelationRule.java @@ -24,35 +24,35 @@ import java.util.Optional; import java.util.function.Function; import java.util.stream.Collectors; -import org.apache.syncope.common.lib.policy.DefaultPullCorrelationRuleConf; -import org.apache.syncope.common.lib.policy.PullCorrelationRuleConf; +import org.apache.syncope.common.lib.policy.DefaultInboundCorrelationRuleConf; +import org.apache.syncope.common.lib.policy.InboundCorrelationRuleConf; import org.apache.syncope.common.lib.to.Item; import org.apache.syncope.common.lib.to.Provision; import org.apache.syncope.core.persistence.api.dao.search.AnyCond; import org.apache.syncope.core.persistence.api.dao.search.AttrCond; import org.apache.syncope.core.persistence.api.dao.search.SearchCond; -import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRule; -import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRuleConfClass; +import org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRule; +import org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRuleConfClass; import org.identityconnectors.framework.common.objects.Attribute; -import org.identityconnectors.framework.common.objects.SyncDelta; +import org.identityconnectors.framework.common.objects.LiveSyncDelta; -@PullCorrelationRuleConfClass(DefaultPullCorrelationRuleConf.class) -public class DefaultPullCorrelationRule implements PullCorrelationRule { +@InboundCorrelationRuleConfClass(DefaultInboundCorrelationRuleConf.class) +public class DefaultInboundCorrelationRule implements InboundCorrelationRule { - private DefaultPullCorrelationRuleConf conf; + private DefaultInboundCorrelationRuleConf conf; @Override - public void setConf(final PullCorrelationRuleConf conf) { - if (conf instanceof DefaultPullCorrelationRuleConf) { - this.conf = DefaultPullCorrelationRuleConf.class.cast(conf); + public void setConf(final InboundCorrelationRuleConf conf) { + if (conf instanceof DefaultInboundCorrelationRuleConf) { + this.conf = DefaultInboundCorrelationRuleConf.class.cast(conf); } else { throw new IllegalArgumentException( - DefaultPullCorrelationRuleConf.class.getName() + " expected, got " + conf.getClass().getName()); + DefaultInboundCorrelationRuleConf.class.getName() + " expected, got " + conf.getClass().getName()); } } @Override - public SearchCond getSearchCond(final SyncDelta syncDelta, final Provision provision) { + public SearchCond getSearchCond(final LiveSyncDelta syncDelta, final Provision provision) { Map mappingItems = provision.getMapping().getItems().stream(). collect(Collectors.toMap(Item::getIntAttrName, Function.identity())); 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 e36f2ba809b..a95426e8f87 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 @@ -51,7 +51,7 @@ 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.PullActions; +import org.apache.syncope.core.provisioning.api.pushpull.InboundActions; import org.apache.syncope.core.provisioning.api.pushpull.RealmPullResultHandler; import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils; import org.apache.syncope.core.spring.security.AuthContextUtils; @@ -63,7 +63,7 @@ @Transactional(rollbackFor = Throwable.class) public class DefaultRealmPullResultHandler - extends AbstractRealmResultHandler + extends AbstractRealmResultHandler implements RealmPullResultHandler { protected static OpEvent.Outcome and(final OpEvent.Outcome left, final OpEvent.Outcome right) { @@ -105,6 +105,11 @@ public boolean handle(final SyncDelta delta) { LOG.debug("Successfully handled {}", delta); + if (stopRequested) { + LOG.debug("Stop was requested"); + return false; + } + if (profile.getTask().getPullMode() != PullMode.INCREMENTAL) { return true; } @@ -136,7 +141,7 @@ protected void throwIgnoreProvisionException(final SyncDelta delta, final Except } IgnoreProvisionException ipe = null; - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { if (ipe == null) { ipe = action.onError(profile, delta, exception); } @@ -177,7 +182,7 @@ protected OpEvent.Outcome assign(final SyncDelta delta, final OrgUnit orgUnit) return OpEvent.Outcome.SUCCESS; } - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.beforeAssign(profile, delta, realmTO); } @@ -216,7 +221,7 @@ protected OpEvent.Outcome provision(final SyncDelta delta, final OrgUnit orgUnit return OpEvent.Outcome.SUCCESS; } - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.beforeProvision(profile, delta, realmTO); } @@ -253,7 +258,7 @@ protected OpEvent.Outcome create( output = actual; resultStatus = OpEvent.Outcome.SUCCESS; - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.after(profile, delta, actual, result); } @@ -307,7 +312,7 @@ protected OpEvent.Outcome update(final SyncDelta delta, final List realms RealmTO before = binder.getRealmTO(realm, true); try { if (!inLink) { - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.beforeUpdate(profile, delta, before, null); } } @@ -323,7 +328,7 @@ protected OpEvent.Outcome update(final SyncDelta delta, final List realms beforeAttrs); taskExecutor.execute(taskInfos, false, securityProperties.getAdminUser()); - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.after(profile, delta, updated, result); } @@ -389,11 +394,11 @@ protected OpEvent.Outcome deprovision(final SyncDelta delta, final List r RealmTO before = binder.getRealmTO(realm, true); try { if (unlink) { - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.beforeUnassign(profile, delta, before); } } else { - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.beforeDeprovision(profile, delta, before); } } @@ -413,7 +418,7 @@ protected OpEvent.Outcome deprovision(final SyncDelta delta, final List r } output = realmTO; - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.after(profile, delta, realmTO, result); } @@ -480,11 +485,11 @@ protected OpEvent.Outcome link(final SyncDelta delta, final List realms, RealmTO before = binder.getRealmTO(realm, true); try { if (unlink) { - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.beforeUnlink(profile, delta, before); } } else { - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.beforeLink(profile, delta, before); } } @@ -554,7 +559,7 @@ protected OpEvent.Outcome delete(final SyncDelta delta, final List realms result.setStatus(ProvisioningReport.Status.SUCCESS); if (!profile.isDryRun()) { - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.beforeDelete(profile, delta, before); } @@ -599,7 +604,7 @@ protected OpEvent.Outcome delete(final SyncDelta delta, final List realms output = null; resultStatus = OpEvent.Outcome.SUCCESS; - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.after(profile, delta, before, result); } } catch (Exception e) { @@ -652,7 +657,7 @@ protected OpEvent.Outcome doHandle(final SyncDelta delta, final OrgUnit orgUnit) delta.getDeltaType(), delta.getUid().getUidValue(), delta.getObject().getObjectClass()); SyncDelta finalDelta = delta; - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { finalDelta = action.preprocess(profile, finalDelta); } 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 f32c597050a..d6c47516e15 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 @@ -115,6 +115,12 @@ public boolean handle(final String realmKey) { try { realm = realmDAO.findById(realmKey).orElseThrow(() -> new NotFoundException("Realm " + realmKey)); doHandle(realm); + + if (stopRequested) { + LOG.debug("Stop was requested"); + return false; + } + return true; } catch (IgnoreProvisionException e) { ProvisioningReport result = new ProvisioningReport(); 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 97d198e0738..7a1a321765f 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 @@ -55,9 +55,9 @@ import org.apache.syncope.core.provisioning.api.WorkflowResult; import org.apache.syncope.core.provisioning.api.job.JobExecutionException; import org.apache.syncope.core.provisioning.api.propagation.PropagationException; -import org.apache.syncope.core.provisioning.api.pushpull.PullActions; +import org.apache.syncope.core.provisioning.api.pushpull.InboundActions; import org.apache.syncope.core.provisioning.api.pushpull.UserPullResultHandler; -import org.apache.syncope.core.provisioning.api.rules.PullMatch; +import org.apache.syncope.core.provisioning.api.rules.InboundMatch; import org.identityconnectors.framework.common.objects.AttributeUtil; import org.identityconnectors.framework.common.objects.SyncDelta; import org.springframework.beans.factory.annotation.Autowired; @@ -95,7 +95,7 @@ protected AnyTO getAnyTO(final Any any) { @Override protected WorkflowResult update(final AnyUR req) { WorkflowResult> update = - uwfAdapter.update((UserUR) req, null, profile.getExecutor(), getContext()); + uwfAdapter.update((UserUR) req, null, profile.getExecutor(), profile.getContext()); return new WorkflowResult<>(update.getResult().getLeft(), update.getPropByRes(), update.getPerformedTasks()); } @@ -112,7 +112,7 @@ protected AnyTO doCreate(final AnyCR anyCR, final SyncDelta delta) { Set.of(profile.getTask().getResource().getKey()), true, profile.getExecutor(), - getContext()); + profile.getContext()); return userDataBinder.getUserTO(created.getKey()); } @@ -131,7 +131,7 @@ protected AnyUR doUpdate( Set.of(profile.getTask().getResource().getKey()), true, profile.getExecutor(), - getContext()); + profile.getContext()); createRemediationIfNeeded(req, delta, result); @@ -141,11 +141,11 @@ protected AnyUR doUpdate( @Override protected OpEvent.Outcome handleLinkedAccounts( final SyncDelta delta, - final List matches, + final List matches, final Provision provision) throws JobExecutionException { OpEvent.Outcome global = OpEvent.Outcome.SUCCESS; - for (PullMatch match : matches) { + for (InboundMatch match : matches) { if (match.getAny() == null) { LOG.error("Could not find linking user, cannot process match {}", match); return OpEvent.Outcome.FAILURE; @@ -264,11 +264,11 @@ protected OpEvent.Outcome deprovision( try { if (matchingRule == MatchingRule.UNASSIGN) { - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.beforeUnassign(profile, delta, before); } } else if (matchingRule == MatchingRule.DEPROVISION) { - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.beforeDeprovision(profile, delta, before); } } @@ -286,7 +286,7 @@ protected OpEvent.Outcome deprovision( false, profile.getExecutor()); - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.after(profile, delta, before, report); } @@ -370,7 +370,7 @@ protected OpEvent.Outcome provision( } }); - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { if (rule == UnmatchingRule.ASSIGN) { action.beforeAssign(profile, delta, accountTO); } else if (rule == UnmatchingRule.PROVISION) { @@ -395,7 +395,7 @@ protected OpEvent.Outcome provision( Set.of(profile.getTask().getResource().getKey()), true, profile.getExecutor(), - getContext()); + profile.getContext()); LinkedAccountTO created = userDAO.findById(req.getKey()). orElseThrow(() -> new IllegalStateException("Could not find the User just updated")). @@ -406,7 +406,7 @@ protected OpEvent.Outcome provision( output = created; resultStatus = OpEvent.Outcome.SUCCESS; - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.after(profile, delta, created, report); } @@ -502,7 +502,7 @@ protected OpEvent.Outcome update( userUR.getLinkedAccounts().add(new LinkedAccountUR.Builder(). operation(PatchOperation.ADD_REPLACE).linkedAccountTO(update).build()); - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.beforeUpdate(profile, delta, before, userUR); } @@ -515,7 +515,7 @@ protected OpEvent.Outcome update( Set.of(profile.getTask().getResource().getKey()), true, profile.getExecutor(), - getContext()); + profile.getContext()); resultStatus = OpEvent.Outcome.SUCCESS; LinkedAccountTO updated = userDAO.findById(userUR.getKey()). @@ -525,7 +525,7 @@ protected OpEvent.Outcome update( orElse(null); output = updated; - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.after(profile, delta, updated, report); } @@ -589,7 +589,7 @@ protected OpEvent.Outcome delete( if (!profile.isDryRun()) { LinkedAccountTO before = userDataBinder.getLinkedAccountTO(account); - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.beforeDelete(profile, delta, before); } @@ -606,12 +606,12 @@ protected OpEvent.Outcome delete( Set.of(profile.getTask().getResource().getKey()), true, profile.getExecutor(), - getContext()); + profile.getContext()); resultStatus = OpEvent.Outcome.SUCCESS; output = null; - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.after(profile, delta, before, report); } } catch (Exception e) { 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 f99757337b6..0e34ebc67d7 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 @@ -175,7 +175,7 @@ protected void deprovision(final Any any, final ConnectorObject beforeObj, fi @Override protected WorkflowResult update(final AnyUR req) { WorkflowResult> update = - uwfAdapter.update((UserUR) req, null, profile.getExecutor(), getContext()); + uwfAdapter.update((UserUR) req, null, profile.getExecutor(), profile.getContext()); return new WorkflowResult<>(update.getResult().getLeft(), update.getPropByRes(), update.getPerformedTasks()); } 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 690332d0c0b..d115862a369 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 @@ -56,19 +56,20 @@ import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.persistence.api.entity.Realm; 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.InboundCorrelationRuleEntity; import org.apache.syncope.core.provisioning.api.Connector; import org.apache.syncope.core.provisioning.api.IntAttrName; import org.apache.syncope.core.provisioning.api.IntAttrNameParser; import org.apache.syncope.core.provisioning.api.VirAttrHandler; import org.apache.syncope.core.provisioning.api.data.ItemTransformer; -import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRule; -import org.apache.syncope.core.provisioning.api.rules.PullMatch; +import org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRule; +import org.apache.syncope.core.provisioning.api.rules.InboundMatch; import org.apache.syncope.core.provisioning.java.utils.MappingUtils; import org.apache.syncope.core.spring.implementation.ImplementationManager; import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.AttributeUtil; import org.identityconnectors.framework.common.objects.ConnectorObject; +import org.identityconnectors.framework.common.objects.LiveSyncDelta; import org.identityconnectors.framework.common.objects.Name; import org.identityconnectors.framework.common.objects.ObjectClass; import org.identityconnectors.framework.common.objects.SearchResult; @@ -112,7 +113,7 @@ public class InboundMatcher { protected final AnyUtilsFactory anyUtilsFactory; - protected final Map perContextPullCorrelationRules = new ConcurrentHashMap<>(); + protected final Map perContextInboundCorrelationRules = new ConcurrentHashMap<>(); public InboundMatcher( final UserDAO userDAO, @@ -140,7 +141,7 @@ public InboundMatcher( this.anyUtilsFactory = anyUtilsFactory; } - public Optional match( + public Optional match( final AnyType anyType, final String nameValue, final ExternalResource resource, @@ -181,7 +182,7 @@ public boolean handle(final ConnectorObject connectorObject) { LOG.warn("While searching for {} ...", nameValue, t); } - Optional result = Optional.empty(); + Optional result = Optional.empty(); if (found.isEmpty()) { LOG.debug("No {} found on {} with {} {}", provision.get().getObjectClass(), resource, Name.NAME, nameValue); @@ -193,7 +194,7 @@ public boolean handle(final ConnectorObject connectorObject) { ConnectorObject connObj = found.iterator().next(); try { - List matches = match( + List matches = match( new SyncDeltaBuilder(). setToken(new SyncToken("")). setDeltaType(SyncDeltaType.CREATE_OR_UPDATE). @@ -210,7 +211,7 @@ public boolean handle(final ConnectorObject connectorObject) { } result = matches.stream().filter(match -> match.getAny() != null).findFirst(); - result.ifPresent(pullMatch -> virAttrHandler.setValues(pullMatch.getAny(), connObj)); + result.ifPresent(inboundMatch -> virAttrHandler.setValues(inboundMatch.getAny(), connObj)); } } catch (IllegalArgumentException e) { LOG.warn(e.getMessage()); @@ -227,7 +228,7 @@ protected List getTransformers(final Item item) { collect(Collectors.toList()); } - public List matchByConnObjectKeyValue( + public List matchByConnObjectKeyValue( final Item connObjectKeyItem, final String connObjectKeyValue, final AnyTypeKind anyTypeKind, @@ -247,7 +248,7 @@ public List matchByConnObjectKeyValue( } } - List noMatchResult = List.of(PullCorrelationRule.NO_MATCH); + List noMatchResult = List.of(InboundCorrelationRule.NO_MATCH); IntAttrName intAttrName; try { @@ -332,26 +333,26 @@ public List matchByConnObjectKeyValue( } } - List result = anys.stream(). - map(any -> new PullMatch(MatchType.ANY, any)). + List result = anys.stream(). + map(any -> new InboundMatch(MatchType.ANY, any)). toList(); if (resource != null) { userDAO.findLinkedAccount(resource, finalConnObjectKeyValue). - map(account -> new PullMatch(MatchType.LINKED_ACCOUNT, account)). + map(account -> new InboundMatch(MatchType.LINKED_ACCOUNT, account)). ifPresent(result::add); } return result.isEmpty() ? noMatchResult : result; } - protected List matchByCorrelationRule( + protected List matchByCorrelationRule( final SyncDelta syncDelta, final Provision provision, - final PullCorrelationRule rule, + final InboundCorrelationRule rule, final AnyTypeKind type) { - List result = new ArrayList<>(); + List result = new ArrayList<>(); try { result.addAll(anySearchDAO.search(rule.getSearchCond(syncDelta, provision), type).stream(). @@ -368,19 +369,18 @@ protected List matchByCorrelationRule( return result; } - protected Optional rule(final ExternalResource resource, final Provision provision) { - Optional correlationRule = resource.getPullPolicy() == null + protected Optional rule(final ExternalResource resource, final Provision provision) { + Optional correlationRule = resource.getInboundPolicy() == null ? Optional.empty() - : resource.getPullPolicy().getCorrelationRule(provision.getAnyType()); + : resource.getInboundPolicy().getCorrelationRule(provision.getAnyType()); - Optional rule = Optional.empty(); + Optional rule = Optional.empty(); if (correlationRule.isPresent()) { Implementation impl = correlationRule.get().getImplementation(); try { - rule = ImplementationManager.buildPullCorrelationRule( - impl, - () -> perContextPullCorrelationRules.get(impl.getKey()), - instance -> perContextPullCorrelationRules.put(impl.getKey(), instance)); + rule = ImplementationManager.buildInboundCorrelationRule(impl, + () -> perContextInboundCorrelationRules.get(impl.getKey()), + instance -> perContextInboundCorrelationRules.put(impl.getKey(), instance)); } catch (Exception e) { LOG.error("While building {}", impl, e); } @@ -398,15 +398,15 @@ protected Optional rule(final ExternalResource resource, fi * @param anyTypeKind type kind * @return list of matching users' / groups' / any objects' keys */ - public List match( + public List match( final SyncDelta syncDelta, final ExternalResource resource, final Provision provision, final AnyTypeKind anyTypeKind) { - Optional rule = rule(resource, provision); + Optional rule = rule(resource, provision); - List result = List.of(); + List result = List.of(); try { if (rule.isPresent()) { result = matchByCorrelationRule(syncDelta, provision, rule.get(), anyTypeKind); @@ -426,7 +426,7 @@ public List match( } } if (connObjectKeyValue == null) { - result = List.of(PullCorrelationRule.NO_MATCH); + result = List.of(InboundCorrelationRule.NO_MATCH); } else { result = matchByConnObjectKeyValue( connObjectKeyItem.get(), @@ -455,7 +455,7 @@ public List match( * @return list of matching realms' keys. */ @Transactional(readOnly = true) - public List match(final SyncDelta syncDelta, final OrgUnit orgUnit) { + public List match(final LiveSyncDelta syncDelta, final OrgUnit orgUnit) { String connObjectKey = null; Optional connObjectKeyItem = orgUnit.getConnObjectKeyItem(); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/KafkaInboundActions.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/KafkaInboundActions.java new file mode 100644 index 00000000000..21c8bff075e --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/KafkaInboundActions.java @@ -0,0 +1,44 @@ +/* + * 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; + +import java.util.Set; +import org.apache.syncope.common.lib.to.OrgUnit; +import org.apache.syncope.common.lib.to.Provision; +import org.apache.syncope.core.provisioning.api.pushpull.InboundActions; +import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile; + +public class KafkaInboundActions implements InboundActions { + + /** + * These are attributes added by the ConnId Kafka connector's livesync() implementation + */ + protected static final Set KAFKA_CONNECTOR_ATTRS = Set.of( + "record.timestamp", "record.headers", "record.value"); + + @Override + public Set moreAttrsToGet(final ProvisioningProfile profile, final Provision provision) { + return KAFKA_CONNECTOR_ATTRS; + } + + @Override + public Set moreAttrsToGet(final ProvisioningProfile profile, final OrgUnit orgUnit) { + return KAFKA_CONNECTOR_ATTRS; + } +} 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 9f7aee856e6..43bb588e790 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 @@ -41,16 +41,16 @@ import org.apache.syncope.core.provisioning.api.Connector; import org.apache.syncope.core.provisioning.api.UserProvisioningManager; import org.apache.syncope.core.provisioning.api.job.JobExecutionException; +import org.apache.syncope.core.provisioning.api.pushpull.InboundActions; import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile; -import org.apache.syncope.core.provisioning.api.pushpull.PullActions; -import org.apache.syncope.core.provisioning.api.rules.PullMatch; +import org.apache.syncope.core.provisioning.api.rules.InboundMatch; import org.apache.syncope.core.spring.implementation.InstanceScope; import org.apache.syncope.core.spring.implementation.SyncopeImplementation; import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.ConnectorObject; +import org.identityconnectors.framework.common.objects.LiveSyncDelta; import org.identityconnectors.framework.common.objects.ObjectClass; import org.identityconnectors.framework.common.objects.OperationOptionsBuilder; -import org.identityconnectors.framework.common.objects.SyncDelta; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -64,7 +64,7 @@ * @see org.apache.syncope.core.provisioning.java.propagation.LDAPMembershipPropagationActions */ @SyncopeImplementation(scope = InstanceScope.PER_CONTEXT) -public class LDAPMembershipPullActions implements PullActions { +public class LDAPMembershipPullActions implements InboundActions { protected static final Logger LOG = LoggerFactory.getLogger(LDAPMembershipPullActions.class); @@ -107,7 +107,7 @@ protected String getGroupMembershipAttrName(final Connector connector) { * @return value of attribute returned by * {@link #getGroupMembershipAttrName} */ - protected List getMembAttrValues(final SyncDelta delta, final Connector connector) { + protected List getMembAttrValues(final LiveSyncDelta delta, final Connector connector) { String groupMemberName = getGroupMembershipAttrName(connector); // first, try to read the configured attribute from delta, returned by the ongoing pull @@ -145,12 +145,12 @@ protected List getMembAttrValues(final SyncDelta delta, final Connector @Override public void beforeUpdate( final ProvisioningProfile profile, - final SyncDelta delta, + final LiveSyncDelta delta, final EntityTO entity, final AnyUR anyUR) throws JobExecutionException { if (!(entity instanceof GroupTO)) { - PullActions.super.beforeUpdate(profile, delta, entity, anyUR); + InboundActions.super.beforeUpdate(profile, delta, entity, anyUR); return; } @@ -171,24 +171,24 @@ public void beforeUpdate( @Override public void after( final ProvisioningProfile profile, - final SyncDelta delta, + final LiveSyncDelta delta, final EntityTO entity, final ProvisioningReport result) throws JobExecutionException { if (!(entity instanceof GroupTO)) { - PullActions.super.after(profile, delta, entity, result); + InboundActions.super.after(profile, delta, entity, result); return; } Optional provision = profile.getTask().getResource(). getProvisionByAnyType(AnyTypeKind.USER.name()).filter(p -> p.getMapping() != null); if (provision.isEmpty()) { - PullActions.super.after(profile, delta, entity, result); + InboundActions.super.after(profile, delta, entity, result); return; } getMembAttrValues(delta, profile.getConnector()).forEach(membValue -> { - Optional match = inboundMatcher.match( + Optional match = inboundMatcher.match( anyTypeDAO.getUser(), membValue.toString(), profile.getTask().getResource(), 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 2bb2ff3d25a..fcfd85e5601 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 @@ -30,22 +30,22 @@ import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.CipherAlgorithm; import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.provisioning.api.pushpull.InboundActions; import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile; -import org.apache.syncope.core.provisioning.api.pushpull.PullActions; import org.identityconnectors.common.security.SecurityUtil; import org.identityconnectors.framework.common.objects.AttributeUtil; +import org.identityconnectors.framework.common.objects.LiveSyncDelta; import org.identityconnectors.framework.common.objects.OperationalAttributes; -import org.identityconnectors.framework.common.objects.SyncDelta; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; /** - * A {@link org.apache.syncope.core.provisioning.api.pushpull.PullActions} implementation which allows the ability to + * A {@link org.apache.syncope.core.provisioning.api.pushpull.InboundActions} implementation which allows the ability to * import passwords from an LDAP backend that are hashed. */ -public class LDAPPasswordPullActions implements PullActions { +public class LDAPPasswordPullActions implements InboundActions { protected static final Logger LOG = LoggerFactory.getLogger(LDAPPasswordPullActions.class); @@ -57,7 +57,7 @@ public Set moreAttrsToGet(final ProvisioningProfile profile, final if (AnyTypeKind.USER.name().equals(provision.getAnyType())) { return Set.of(OperationalAttributes.PASSWORD_NAME); } - return PullActions.super.moreAttrsToGet(profile, provision); + return InboundActions.super.moreAttrsToGet(profile, provision); } protected Optional> parseEncodedPassword(final String password) { @@ -80,7 +80,7 @@ protected Optional> parseEncodedPassword(final Str @Override public void after( final ProvisioningProfile profile, - final SyncDelta delta, + final LiveSyncDelta delta, final EntityTO entity, final ProvisioningReport result) { diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LiveSyncJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LiveSyncJobDelegate.java new file mode 100644 index 00000000000..b2b377b1fbd --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LiveSyncJobDelegate.java @@ -0,0 +1,393 @@ +/* + * 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; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Stream; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.to.Item; +import org.apache.syncope.common.lib.to.OrgUnit; +import org.apache.syncope.common.lib.to.Provision; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.ConflictResolutionAction; +import org.apache.syncope.common.lib.types.IdMImplementationType; +import org.apache.syncope.common.lib.types.OpEvent; +import org.apache.syncope.common.lib.types.PullMode; +import org.apache.syncope.common.lib.types.ResourceOperation; +import org.apache.syncope.common.lib.types.TaskType; +import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; +import org.apache.syncope.core.persistence.api.dao.GroupDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +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.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.InboundPolicy; +import org.apache.syncope.core.persistence.api.entity.task.AnyTemplatePullTask; +import org.apache.syncope.core.persistence.api.entity.task.LiveSyncTask; +import org.apache.syncope.core.persistence.api.entity.task.PullTask; +import org.apache.syncope.core.persistence.api.entity.task.SchedTask; +import org.apache.syncope.core.persistence.api.entity.task.TaskExec; +import org.apache.syncope.core.persistence.api.utils.ExceptionUtils2; +import org.apache.syncope.core.provisioning.api.LiveSyncDeltaMapper; +import org.apache.syncope.core.provisioning.api.ProvisionSorter; +import org.apache.syncope.core.provisioning.api.job.JobExecutionContext; +import org.apache.syncope.core.provisioning.api.job.JobExecutionException; +import org.apache.syncope.core.provisioning.api.job.StoppableSchedTaskJobDelegate; +import org.apache.syncope.core.provisioning.api.pushpull.AnyObjectPullResultHandler; +import org.apache.syncope.core.provisioning.api.pushpull.GroupPullResultHandler; +import org.apache.syncope.core.provisioning.api.pushpull.InboundActions; +import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile; +import org.apache.syncope.core.provisioning.api.pushpull.RealmPullResultHandler; +import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullResultHandler; +import org.apache.syncope.core.provisioning.api.pushpull.UserPullResultHandler; +import org.apache.syncope.core.provisioning.java.job.TaskJob; +import org.apache.syncope.core.provisioning.java.utils.MappingUtils; +import org.apache.syncope.core.spring.ApplicationContextProvider; +import org.apache.syncope.core.spring.implementation.ImplementationManager; +import org.identityconnectors.framework.common.objects.ObjectClass; +import org.identityconnectors.framework.common.objects.OperationOptions; +import org.identityconnectors.framework.common.objects.SyncDelta; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +public class LiveSyncJobDelegate + extends AbstractProvisioningJobDelegate + implements StoppableSchedTaskJobDelegate { + + protected static record LiveSyncInfo( + Provision provision, + OrgUnit orgUnit, + ObjectClass objectClass, + AnyTypeKind anyTypeKind, + PlainSchema uidOnCreate, + SyncopePullResultHandler handler, + OperationOptions options) { + + } + + @Autowired + protected PlainSchemaDAO plainSchemaDAO; + + @Autowired + protected VirSchemaDAO virSchemaDAO; + + @Autowired + protected GroupDAO groupDAO; + + @Autowired + protected PlainAttrValidationManager validator; + + @Autowired + protected InboundMatcher inboundMatcher; + + @Autowired + protected LiveSyncTaskExecSaver liveSyncTaskExecSaver; + + protected ProvisioningProfile profile; + + protected LiveSyncDeltaMapper mapper; + + protected List infos; + + protected final Map perContextActions = new ConcurrentHashMap<>(); + + protected volatile boolean stopRequested = false; + + protected RealmPullResultHandler buildRealmHandler() { + return ApplicationContextProvider.getBeanFactory().createBean(DefaultRealmPullResultHandler.class); + } + + protected AnyObjectPullResultHandler buildAnyObjectHandler() { + return ApplicationContextProvider.getBeanFactory().createBean(DefaultAnyObjectPullResultHandler.class); + } + + protected UserPullResultHandler buildUserHandler() { + return ApplicationContextProvider.getBeanFactory().createBean(DefaultUserPullResultHandler.class); + } + + protected GroupPullResultHandler buildGroupHandler() { + return ApplicationContextProvider.getBeanFactory().createBean(DefaultGroupPullResultHandler.class); + } + + @Override + protected void init( + final TaskType taskType, + final String taskKey, + final JobExecutionContext context) throws JobExecutionException { + + super.init(taskType, taskKey, context); + + Implementation impl = Optional.ofNullable(task.getLiveSyncDeltaMapper()). + orElseThrow(() -> new JobExecutionException( + "No " + LiveSyncDeltaMapper.class.getSimpleName() + " provided, aborting")); + try { + mapper = ImplementationManager.build(impl); + } catch (Exception e) { + throw new JobExecutionException( + "Could not build " + IdMImplementationType.LIVE_SYNC_DELTA_MAPPER + " " + impl.getKey(), e); + } + + PullTask pullTask = entityFactory.newEntity(PullTask.class); + pullTask.setName(task.getName()); + pullTask.setResource(task.getResource()); + pullTask.setMatchingRule(task.getMatchingRule()); + pullTask.setUnmatchingRule(task.getUnmatchingRule()); + pullTask.setPullMode(PullMode.INCREMENTAL); + pullTask.setPerformCreate(task.isPerformCreate()); + pullTask.setPerformUpdate(task.isPerformUpdate()); + pullTask.setPerformDelete(task.isPerformDelete()); + pullTask.setSyncStatus(task.isSyncStatus()); + pullTask.setDestinationRealm(task.getDestinationRealm()); + pullTask.setRemediation(task.isRemediation()); + task.getTemplates().forEach(atlst -> { + AnyTemplatePullTask atpt = entityFactory.newEntity(AnyTemplatePullTask.class); + atpt.setAnyType(atlst.getAnyType()); + atpt.setPullTask(pullTask); + + pullTask.add(atpt); + atpt.set(atlst.get()); + }); + + List actions = new ArrayList<>(); + task.getActions().forEach(action -> { + try { + actions.add(ImplementationManager.build( + action, + () -> perContextActions.get(action.getKey()), + instance -> perContextActions.put(action.getKey(), instance))); + } catch (Exception e) { + LOG.warn("While building {}", action, e); + } + }); + profile = new ProvisioningProfile<>( + connector, + taskType, + pullTask, + Optional.ofNullable(task.getResource().getInboundPolicy()). + map(InboundPolicy::getConflictResolutionAction). + orElse(ConflictResolutionAction.IGNORE), + actions, + executor, + context.isDryRun()) { + + @Override + public String getContext() { + return taskType + " Task " + taskKey + " '" + task.getName() + "'"; + } + }; + + infos = new ArrayList<>(); + + // First realms... + if (task.getResource().getOrgUnit() != null) { + setStatus("Pulling " + task.getResource().getOrgUnit().getObjectClass()); + + OrgUnit orgUnit = task.getResource().getOrgUnit(); + + Set moreAttrsToGet = new HashSet<>(); + profile.getActions().forEach(a -> moreAttrsToGet.addAll(a.moreAttrsToGet(profile, orgUnit))); + OperationOptions options = MappingUtils.buildOperationOptions( + MappingUtils.getInboundItems(orgUnit.getItems().stream()), moreAttrsToGet.toArray(String[]::new)); + + RealmPullResultHandler handler = buildRealmHandler(); + handler.setProfile(profile); + + infos.add(new LiveSyncInfo( + null, + orgUnit, + new ObjectClass(orgUnit.getObjectClass()), + null, + null, + handler, + options)); + } + + // ...then provisions for any types + ProvisionSorter provisionSorter = getProvisionSorter(task); + + for (Provision provision : task.getResource().getProvisions().stream(). + filter(provision -> provision.getMapping() != null).sorted(provisionSorter). + toList()) { + + AnyType anyType = anyTypeDAO.findById(provision.getAnyType()). + orElseThrow(() -> new NotFoundException("AnyType" + provision.getAnyType())); + + PlainSchema uidOnCreate = null; + if (provision.getUidOnCreate() != null) { + uidOnCreate = plainSchemaDAO.findById(provision.getUidOnCreate()). + orElseThrow(() -> new NotFoundException("PlainSchema " + provision.getUidOnCreate())); + } + + SyncopePullResultHandler handler; + switch (anyType.getKind()) { + case USER: + handler = buildUserHandler(); + break; + + case GROUP: + handler = buildGroupHandler(); + break; + + case ANY_OBJECT: + default: + handler = buildAnyObjectHandler(); + } + handler.setProfile(profile); + + Set moreAttrsToGet = new HashSet<>(); + profile.getActions().forEach(a -> moreAttrsToGet.addAll(a.moreAttrsToGet(profile, provision))); + Stream mapItems = Stream.concat( + MappingUtils.getInboundItems(provision.getMapping().getItems().stream()), + virSchemaDAO.findByResourceAndAnyType( + task.getResource().getKey(), anyType.getKey()).stream(). + map(VirSchema::asLinkingMappingItem)); + OperationOptions options = MappingUtils.buildOperationOptions( + mapItems, moreAttrsToGet.toArray(String[]::new)); + + infos.add(new LiveSyncInfo( + provision, + null, + new ObjectClass(provision.getObjectClass()), + anyType.getKind(), + uidOnCreate, + handler, + options)); + } + + setStatus("Initialization completed"); + } + + @Override + public void stop() { + stopRequested = true; + } + + @Transactional + @Override + public void execute( + final TaskType taskType, + final String taskKey, + final JobExecutionContext context) throws JobExecutionException { + + init(taskType, taskKey, context); + + setStatus("Initialization completed"); + + doExecute(context); + + end(); + } + + @Override + protected String doExecute(final JobExecutionContext context) throws JobExecutionException { + if (infos.isEmpty()) { + LOG.info("Nothing to live sync on, aborting..."); + return StringUtils.EMPTY; + } + + LOG.debug("Executing live sync on {}", task.getResource()); + + while (!stopRequested) { + profile.getResults().clear(); + + TaskExec execution = initExecution(); + execution.setTask(null); + + String message; + String status; + OpEvent.Outcome result; + try { + infos.forEach(info -> { + setStatus("Live syncing " + info.objectClass().getObjectClassValue()); + + profile.getConnector().livesync( + info.objectClass(), + liveSyncDelta -> { + try { + LOG.debug("LiveSyncDelta: {}", liveSyncDelta); + SyncDelta syncDelta = info.provision() == null + ? mapper.map(liveSyncDelta, info.orgUnit()) + : mapper.map(liveSyncDelta, info.provision()); + LOG.debug("Mapped SyncDelta: {}", syncDelta); + + return info.handler().handle(syncDelta); + } catch (Exception e) { + LOG.error("While live syncing from {} with {}", + task.getResource().getKey(), liveSyncDelta, e); + return false; + } + }, + info.options()); + + if (info.uidOnCreate() != null) { + AnyUtils anyUtils = anyUtilsFactory.getInstance(info.anyTypeKind()); + profile.getResults().stream(). + filter(r -> r.getUidValue() != null && r.getKey() != null + && r.getOperation() == ResourceOperation.CREATE + && r.getAnyType().equals(info.provision().getAnyType())). + forEach(r -> anyUtils.addAttr( + validator, + r.getKey(), + info.uidOnCreate(), + r.getUidValue())); + } + + if (info.anyTypeKind() == AnyTypeKind.GROUP) { + try { + PullJobDelegate.setGroupOwners( + (GroupPullResultHandler) info.handler(), + groupDAO, + anyTypeDAO, + inboundMatcher, + profile); + } catch (Exception e) { + LOG.error("While setting group owners", e); + } + } + }); + + message = createReport(profile.getResults(), task.getResource(), context.isDryRun()); + status = TaskJob.Status.SUCCESS.name(); + result = OpEvent.Outcome.SUCCESS; + } catch (Throwable t) { + LOG.error("While executing task {}", task.getKey(), t); + + message = ExceptionUtils2.getFullStackTrace(t); + status = TaskJob.Status.FAILURE.name(); + result = OpEvent.Outcome.FAILURE; + } + + if (!profile.getResults().isEmpty()) { + liveSyncTaskExecSaver.save(task.getKey(), execution, message, status, result, this::hasToBeRegistered); + } + } + + return StringUtils.EMPTY; + } +} diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LiveSyncTaskExecSaver.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LiveSyncTaskExecSaver.java new file mode 100644 index 00000000000..8ac40d7e49a --- /dev/null +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LiveSyncTaskExecSaver.java @@ -0,0 +1,107 @@ +/* + * 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; + +import java.time.OffsetDateTime; +import java.util.function.Function; +import org.apache.syncope.common.lib.types.OpEvent; +import org.apache.syncope.common.lib.types.TaskType; +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.entity.task.LiveSyncTask; +import org.apache.syncope.core.persistence.api.entity.task.SchedTask; +import org.apache.syncope.core.persistence.api.entity.task.TaskExec; +import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory; +import org.apache.syncope.core.provisioning.api.AuditManager; +import org.apache.syncope.core.provisioning.api.job.JobExecutionException; +import org.apache.syncope.core.provisioning.api.notification.NotificationManager; +import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +public class LiveSyncTaskExecSaver { + + protected final TaskDAO taskDAO; + + protected final TaskExecDAO taskExecDAO; + + protected final TaskUtilsFactory taskUtilsFactory; + + protected final NotificationManager notificationManager; + + protected final AuditManager auditManager; + + public LiveSyncTaskExecSaver( + final TaskDAO taskDAO, + final TaskExecDAO taskExecDAO, + final TaskUtilsFactory taskUtilsFactory, + final NotificationManager notificationManager, + final AuditManager auditManager) { + + this.taskDAO = taskDAO; + this.taskExecDAO = taskExecDAO; + this.taskUtilsFactory = taskUtilsFactory; + this.notificationManager = notificationManager; + this.auditManager = auditManager; + } + + @Transactional(propagation = Propagation.REQUIRES_NEW) + public void save( + final String taskKey, + final TaskExec execution, + final String message, + final String status, + final OpEvent.Outcome result, + final Function, Boolean> hasToBeRegistered) throws JobExecutionException { + + LiveSyncTask task = (LiveSyncTask) taskDAO.findById(TaskType.LIVE_SYNC, taskKey). + orElseThrow(() -> new JobExecutionException("Not found: " + TaskType.LIVE_SYNC + " Task " + taskKey)); + + execution.setTask(task); + execution.setMessage(message); + execution.setStatus(status); + execution.setEnd(OffsetDateTime.now()); + + if (hasToBeRegistered.apply(execution)) { + taskExecDAO.saveAndAdd(TaskType.LIVE_SYNC, task.getKey(), execution); + } + task = taskDAO.save(task); + + notificationManager.createTasks( + execution.getExecutor(), + OpEvent.CategoryType.TASK, + this.getClass().getSimpleName(), + null, + this.getClass().getSimpleName(), + result, + task, + execution); + + auditManager.audit( + AuthContextUtils.getDomain(), + execution.getExecutor(), + OpEvent.CategoryType.TASK, + task.getClass().getSimpleName(), + null, + this.getClass().getSimpleName(), + result, + task, + execution); + } +} diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java index 467ba0fa4b6..1013f4f8eb0 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java @@ -35,34 +35,34 @@ import org.apache.syncope.common.lib.to.Provision; import org.apache.syncope.common.lib.types.ConflictResolutionAction; import org.apache.syncope.common.lib.types.ResourceOperation; +import org.apache.syncope.common.lib.types.TaskType; import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; +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.dao.NotFoundException; 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.AnyType; 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.VirSchema; import org.apache.syncope.core.persistence.api.entity.group.Group; -import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy; +import org.apache.syncope.core.persistence.api.entity.policy.InboundPolicy; import org.apache.syncope.core.persistence.api.entity.task.PullTask; 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.ProvisionSorter; import org.apache.syncope.core.provisioning.api.job.JobExecutionContext; import org.apache.syncope.core.provisioning.api.job.JobExecutionException; +import org.apache.syncope.core.provisioning.api.job.StoppableSchedTaskJobDelegate; import org.apache.syncope.core.provisioning.api.pushpull.AnyObjectPullResultHandler; import org.apache.syncope.core.provisioning.api.pushpull.GroupPullResultHandler; +import org.apache.syncope.core.provisioning.api.pushpull.InboundActions; import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile; -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.ReconFilterBuilder; import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullExecutor; import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullResultHandler; import org.apache.syncope.core.provisioning.api.pushpull.UserPullResultHandler; -import org.apache.syncope.core.provisioning.api.rules.PullMatch; import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils; import org.apache.syncope.core.provisioning.java.utils.MappingUtils; import org.apache.syncope.core.spring.ApplicationContextProvider; @@ -73,7 +73,41 @@ import org.identityconnectors.framework.common.objects.SyncToken; import org.springframework.beans.factory.annotation.Autowired; -public class PullJobDelegate extends AbstractProvisioningJobDelegate implements SyncopePullExecutor { +public class PullJobDelegate + extends AbstractProvisioningJobDelegate + implements SyncopePullExecutor, StoppableSchedTaskJobDelegate { + + public static void setGroupOwners( + final GroupPullResultHandler ghandler, + final GroupDAO groupDAO, + final AnyTypeDAO anyTypeDAO, + final InboundMatcher inboundMatcher, + final ProvisioningProfile profile) { + + ghandler.getGroupOwnerMap().forEach((groupKey, ownerKey) -> { + Group group = groupDAO.findById(groupKey).orElseThrow(() -> new NotFoundException("Group " + groupKey)); + + if (StringUtils.isBlank(ownerKey)) { + group.setGroupOwner(null); + group.setUserOwner(null); + } else { + inboundMatcher.match( + anyTypeDAO.getUser(), + ownerKey, + profile.getTask().getResource(), + profile.getConnector()).ifPresentOrElse( + userMatch -> group.setUserOwner((User) userMatch.getAny()), + () -> inboundMatcher.match( + anyTypeDAO.getGroup(), + ownerKey, + profile.getTask().getResource(), + profile.getConnector()). + ifPresent(groupMatch -> group.setGroupOwner((Group) groupMatch.getAny()))); + } + + groupDAO.save(group); + }); + } @Autowired protected GroupDAO groupDAO; @@ -87,22 +121,21 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate i @Autowired protected InboundMatcher inboundMatcher; - @Autowired - protected AnyUtilsFactory anyUtilsFactory; - @Autowired protected PlainAttrValidationManager validator; protected final Map latestSyncTokens = Collections.synchronizedMap(new HashMap<>()); - protected ProvisioningProfile profile; + protected ProvisioningProfile profile; protected final Map> handled = new HashMap<>(); - protected final Map perContextActions = new ConcurrentHashMap<>(); + protected final Map perContextActions = new ConcurrentHashMap<>(); protected Optional perContextReconFilterBuilder = Optional.empty(); + protected PullResultHandlerDispatcher dispatcher; + @Override public void setLatestSyncToken(final String objectClass, final SyncToken latestSyncToken) { latestSyncTokens.put(objectClass, latestSyncToken); @@ -130,38 +163,8 @@ public void reportHandled(final String objectClass, final Name name) { } } - protected void setGroupOwners(final GroupPullResultHandler ghandler) { - ghandler.getGroupOwnerMap().forEach((groupKey, ownerKey) -> { - Group group = groupDAO.findById(groupKey). - orElseThrow(() -> new NotFoundException("Group " + groupKey)); - - if (StringUtils.isBlank(ownerKey)) { - group.setGroupOwner(null); - group.setUserOwner(null); - } else { - Optional match = inboundMatcher.match( - anyTypeDAO.getUser(), - ownerKey, - profile.getTask().getResource(), - profile.getConnector()); - if (match.isPresent()) { - group.setUserOwner((User) match.get().getAny()); - } else { - inboundMatcher.match( - anyTypeDAO.getGroup(), - ownerKey, - profile.getTask().getResource(), - profile.getConnector()). - ifPresent(groupMatch -> group.setGroupOwner((Group) groupMatch.getAny())); - } - } - - groupDAO.save(group); - }); - } - - protected List getPullActions(final List impls) { - List result = new ArrayList<>(); + protected List getInboundActions(final List impls) { + List result = new ArrayList<>(); impls.forEach(impl -> { try { @@ -177,9 +180,9 @@ protected List getPullActions(final List return result; } - protected ReconFilterBuilder getReconFilterBuilder(final PullTask pullTask) throws ClassNotFoundException { + protected ReconFilterBuilder getReconFilterBuilder(final PullTask task) throws ClassNotFoundException { return ImplementationManager.build( - pullTask.getReconFilterBuilder(), + task.getReconFilterBuilder(), () -> perContextReconFilterBuilder.orElse(null), instance -> perContextReconFilterBuilder = Optional.of(instance)); } @@ -201,46 +204,56 @@ protected GroupPullResultHandler buildGroupHandler() { } @Override - protected String doExecuteProvisioning( - final PullTask pullTask, - final Connector connector, - final boolean dryRun, - final String executor, + protected void init( + final TaskType taskType, + final String taskKey, final JobExecutionContext context) throws JobExecutionException { - LOG.debug("Executing pull on {}", pullTask.getResource()); + super.init(taskType, taskKey, context); - profile = new ProvisioningProfile<>(connector, pullTask); - profile.getActions().addAll(getPullActions(pullTask.getActions())); - profile.setDryRun(dryRun); - profile.setConflictResolutionAction( - Optional.ofNullable(pullTask.getResource().getPullPolicy()). - map(PullPolicy::getConflictResolutionAction). - orElse(ConflictResolutionAction.IGNORE)); - profile.setExecutor(executor); + profile = new ProvisioningProfile<>( + connector, + taskType, + task, + Optional.ofNullable(task.getResource().getInboundPolicy()). + map(InboundPolicy::getConflictResolutionAction). + orElse(ConflictResolutionAction.IGNORE), + getInboundActions(task.getActions()), + executor, + context.isDryRun()); - PullResultHandlerDispatcher dispatcher = new PullResultHandlerDispatcher(profile, this); + latestSyncTokens.clear(); + } + + @Override + public void stop() { + Optional.ofNullable(dispatcher).ifPresent(PullResultHandlerDispatcher::stop); + } + + @Override + protected String doExecute(final JobExecutionContext context) throws JobExecutionException { + LOG.debug("Executing pull on {}", task.getResource()); + + dispatcher = new PullResultHandlerDispatcher(profile, this); latestSyncTokens.clear(); if (!profile.isDryRun()) { - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.beforeAll(profile); } } - setStatus("Initialization completed"); - // First realms... - if (pullTask.getResource().getOrgUnit() != null) { - setStatus("Pulling " + pullTask.getResource().getOrgUnit().getObjectClass()); + if (task.getResource().getOrgUnit() != null) { + setStatus("Pulling " + task.getResource().getOrgUnit().getObjectClass()); - OrgUnit orgUnit = pullTask.getResource().getOrgUnit(); + OrgUnit orgUnit = task.getResource().getOrgUnit(); Set moreAttrsToGet = new HashSet<>(); profile.getActions().forEach(a -> moreAttrsToGet.addAll(a.moreAttrsToGet(profile, orgUnit))); OperationOptions options = MappingUtils.buildOperationOptions( - MappingUtils.getPullItems(orgUnit.getItems().stream()), moreAttrsToGet.toArray(String[]::new)); + MappingUtils.getInboundItems(orgUnit.getItems().stream()), moreAttrsToGet.toArray(String[]::new)); dispatcher.addHandlerSupplier(orgUnit.getObjectClass(), () -> { RealmPullResultHandler handler = buildRealmHandler(); @@ -249,9 +262,9 @@ protected String doExecuteProvisioning( }); try { - switch (pullTask.getPullMode()) { + switch (task.getPullMode()) { case INCREMENTAL: - if (!dryRun) { + if (!context.isDryRun()) { latestSyncTokens.put( orgUnit.getObjectClass(), ConnObjectUtils.toSyncToken(orgUnit.getSyncToken())); @@ -262,16 +275,16 @@ protected String doExecuteProvisioning( dispatcher, options); - if (!dryRun) { + if (!context.isDryRun()) { orgUnit.setSyncToken( ConnObjectUtils.toString(latestSyncTokens.get(orgUnit.getObjectClass()))); - resourceDAO.save(pullTask.getResource()); + resourceDAO.save(task.getResource()); } break; case FILTERED_RECONCILIATION: connector.filteredReconciliation(new ObjectClass(orgUnit.getObjectClass()), - getReconFilterBuilder(pullTask), + getReconFilterBuilder(task), dispatcher, options); break; @@ -290,10 +303,10 @@ protected String doExecuteProvisioning( } // ...then provisions for any types - ProvisionSorter provisionSorter = getProvisionSorter(pullTask); + ProvisionSorter provisionSorter = getProvisionSorter(task); GroupPullResultHandler ghandler = buildGroupHandler(); - for (Provision provision : pullTask.getResource().getProvisions().stream(). + for (Provision provision : task.getResource().getProvisions().stream(). filter(provision -> provision.getMapping() != null).sorted(provisionSorter). toList()) { @@ -326,16 +339,16 @@ protected String doExecuteProvisioning( Set moreAttrsToGet = new HashSet<>(); profile.getActions().forEach(a -> moreAttrsToGet.addAll(a.moreAttrsToGet(profile, provision))); Stream mapItems = Stream.concat( - MappingUtils.getPullItems(provision.getMapping().getItems().stream()), + MappingUtils.getInboundItems(provision.getMapping().getItems().stream()), virSchemaDAO.findByResourceAndAnyType( - pullTask.getResource().getKey(), anyType.getKey()).stream(). + task.getResource().getKey(), anyType.getKey()).stream(). map(VirSchema::asLinkingMappingItem)); OperationOptions options = MappingUtils.buildOperationOptions( mapItems, moreAttrsToGet.toArray(String[]::new)); - switch (pullTask.getPullMode()) { + switch (task.getPullMode()) { case INCREMENTAL: - if (!dryRun) { + if (!context.isDryRun()) { latestSyncTokens.put( provision.getObjectClass(), ConnObjectUtils.toSyncToken(provision.getSyncToken())); @@ -347,14 +360,14 @@ protected String doExecuteProvisioning( dispatcher, options); - if (!dryRun) { + if (!context.isDryRun()) { setSyncTokens = true; } break; case FILTERED_RECONCILIATION: connector.filteredReconciliation(new ObjectClass(provision.getObjectClass()), - getReconFilterBuilder(pullTask), + getReconFilterBuilder(task), dispatcher, options); break; @@ -370,17 +383,17 @@ protected String doExecuteProvisioning( throw new JobExecutionException("While pulling from connector", t); } finally { if (setSyncTokens) { - latestSyncTokens.forEach((objectClass, syncToken) -> pullTask.getResource(). + latestSyncTokens.forEach((objectClass, syncToken) -> task.getResource(). getProvisionByObjectClass(objectClass). ifPresent(p -> p.setSyncToken(ConnObjectUtils.toString(syncToken)))); - resourceDAO.save(pullTask.getResource()); + resourceDAO.save(task.getResource()); } } } dispatcher.shutdown(); - for (Provision provision : pullTask.getResource().getProvisions().stream(). + for (Provision provision : task.getResource().getProvisions().stream(). filter(provision -> provision.getMapping() != null && provision.getUidOnCreate() != null). sorted(provisionSorter).toList()) { @@ -397,28 +410,27 @@ protected String doExecuteProvisioning( result.getKey(), plainSchemaDAO.findById(provision.getUidOnCreate()). orElseThrow(() -> new NotFoundException("PlainSchema " + provision.getUidOnCreate())), - result.getUidValue()) - ); + result.getUidValue())); } catch (Throwable t) { LOG.error("While setting UID on create", t); } } try { - setGroupOwners(ghandler); + setGroupOwners(ghandler, groupDAO, anyTypeDAO, inboundMatcher, profile); } catch (Exception e) { LOG.error("While setting group owners", e); } if (!profile.isDryRun()) { - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.afterAll(profile); } } setStatus("Pull done"); - String result = createReport(profile.getResults(), pullTask.getResource(), dryRun); + String result = createReport(profile.getResults(), task.getResource(), context.isDryRun()); LOG.debug("Pull result: {}", result); return result; } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullResultHandlerDispatcher.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullResultHandlerDispatcher.java index 7fb2d56a0e9..0fae3815295 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullResultHandlerDispatcher.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullResultHandlerDispatcher.java @@ -20,8 +20,8 @@ import java.util.concurrent.RejectedExecutionException; import org.apache.syncope.core.persistence.api.entity.task.PullTask; +import org.apache.syncope.core.provisioning.api.pushpull.InboundActions; import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile; -import org.apache.syncope.core.provisioning.api.pushpull.PullActions; import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullExecutor; import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullResultHandler; import org.identityconnectors.framework.common.objects.SyncDelta; @@ -29,13 +29,13 @@ import org.springframework.transaction.annotation.Transactional; public class PullResultHandlerDispatcher - extends SyncopeResultHandlerDispatcher + extends SyncopeResultHandlerDispatcher implements SyncResultsHandler { protected final SyncopePullExecutor executor; public PullResultHandlerDispatcher( - final ProvisioningProfile profile, + final ProvisioningProfile profile, final SyncopePullExecutor executor) { super(profile); 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 000887b8159..88d3213b1be 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 @@ -29,6 +29,7 @@ import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.to.Provision; import org.apache.syncope.common.lib.types.ConflictResolutionAction; +import org.apache.syncope.common.lib.types.TaskType; import org.apache.syncope.core.persistence.api.dao.AnyDAO; import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; import org.apache.syncope.core.persistence.api.dao.NotFoundException; @@ -36,7 +37,6 @@ import org.apache.syncope.core.persistence.api.dao.search.SearchCond; import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.AnyType; -import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.Implementation; import org.apache.syncope.core.persistence.api.entity.Realm; @@ -44,10 +44,10 @@ import org.apache.syncope.core.persistence.api.entity.task.PushTask; import org.apache.syncope.core.persistence.api.search.SearchCondConverter; import org.apache.syncope.core.persistence.api.search.SearchCondVisitor; -import org.apache.syncope.core.provisioning.api.Connector; import org.apache.syncope.core.provisioning.api.ProvisionSorter; import org.apache.syncope.core.provisioning.api.job.JobExecutionContext; import org.apache.syncope.core.provisioning.api.job.JobExecutionException; +import org.apache.syncope.core.provisioning.api.job.StoppableSchedTaskJobDelegate; 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; @@ -62,7 +62,9 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; -public class PushJobDelegate extends AbstractProvisioningJobDelegate implements SyncopePushExecutor { +public class PushJobDelegate + extends AbstractProvisioningJobDelegate + implements SyncopePushExecutor, StoppableSchedTaskJobDelegate { @Autowired protected AnySearchDAO searchDAO; @@ -70,9 +72,6 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate i @Autowired protected RealmSearchDAO realmSearchDAO; - @Autowired - protected AnyUtilsFactory anyUtilsFactory; - @Autowired protected SearchCondVisitor searchCondVisitor; @@ -82,6 +81,8 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate i protected final Map perContextActions = new ConcurrentHashMap<>(); + protected PushResultHandlerDispatcher dispatcher; + @Override public void reportHandled(final String anyType, final String key) { MutablePair pair = handled.get(anyType); @@ -154,25 +155,35 @@ protected List getPushActions(final List } @Override - protected String doExecuteProvisioning( - final PushTask pushTask, - final Connector connector, - final boolean dryRun, - final String executor, + protected void init( + final TaskType taskType, + final String taskKey, final JobExecutionContext context) throws JobExecutionException { - LOG.debug("Executing push on {}", pushTask.getResource()); + super.init(taskType, taskKey, context); - profile = new ProvisioningProfile<>(connector, pushTask); - profile.getActions().addAll(getPushActions(pushTask.getActions())); - profile.setDryRun(dryRun); - profile.setConflictResolutionAction( - Optional.ofNullable(pushTask.getResource().getPushPolicy()). + profile = new ProvisioningProfile<>( + connector, + taskType, + task, + Optional.ofNullable(task.getResource().getPushPolicy()). map(PushPolicy::getConflictResolutionAction). - orElse(ConflictResolutionAction.IGNORE)); - profile.setExecutor(executor); + orElse(ConflictResolutionAction.IGNORE), + getPushActions(task.getActions()), + executor, + context.isDryRun()); + } + + @Override + public void stop() { + Optional.ofNullable(dispatcher).ifPresent(PushResultHandlerDispatcher::stop); + } + + @Override + protected String doExecute(final JobExecutionContext context) throws JobExecutionException { + LOG.debug("Executing push on {}", task.getResource()); - PushResultHandlerDispatcher dispatcher = new PushResultHandlerDispatcher(profile, this); + dispatcher = new PushResultHandlerDispatcher(profile, this); if (!profile.isDryRun()) { for (PushActions action : profile.getActions()) { @@ -183,7 +194,7 @@ protected String doExecuteProvisioning( setStatus("Initialization completed"); // First realms... - if (pushTask.getResource().getOrgUnit() != null) { + if (task.getResource().getOrgUnit() != null) { setStatus("Pushing realms"); dispatcher.addHandlerSupplier(SyncopeConstants.REALM_ANYTYPE, () -> { @@ -201,17 +212,17 @@ protected String doExecuteProvisioning( try { result = dispatcher.handle(SyncopeConstants.REALM_ANYTYPE, realms.get(i).getKey()); } catch (Exception e) { - LOG.warn("Failure pushing '{}' on '{}'", realms.get(i), pushTask.getResource(), e); + LOG.warn("Failure pushing '{}' on '{}'", realms.get(i), task.getResource(), e); throw new JobExecutionException( - "While pushing " + realms.get(i) + " on " + pushTask.getResource(), e); + "While pushing " + realms.get(i) + " on " + task.getResource(), e); } } } // ...then provisions for any types - ProvisionSorter provisionSorter = getProvisionSorter(pushTask); + ProvisionSorter provisionSorter = getProvisionSorter(task); - for (Provision provision : pushTask.getResource().getProvisions().stream(). + for (Provision provision : task.getResource().getProvisions().stream(). filter(provision -> provision.getMapping() != null).sorted(provisionSorter). toList()) { @@ -241,7 +252,7 @@ protected String doExecuteProvisioning( return handler; }); - String filter = pushTask.getFilter(anyType.getKey()).orElse(null); + String filter = task.getFilter(anyType.getKey()).orElse(null); SearchCond cond = StringUtils.isBlank(filter) ? anyDAO.getAllMatchingCond() : SearchCondConverter.convert(searchCondVisitor, filter); @@ -260,7 +271,7 @@ protected String doExecuteProvisioning( cond, PageRequest.of(page, AnyDAO.DEFAULT_PAGE_SIZE), anyType.getKind()); - result = doHandle(anys, dispatcher, pushTask.getResource()); + result = doHandle(anys, dispatcher, task.getResource()); } } @@ -274,7 +285,7 @@ protected String doExecuteProvisioning( setStatus("Push done"); - String result = createReport(profile.getResults(), pushTask.getResource(), dryRun); + String result = createReport(profile.getResults(), task.getResource(), context.isDryRun()); LOG.debug("Push result: {}", result); return result; } 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 229648f6564..5a6d65f1e3e 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 @@ -44,8 +44,8 @@ import org.apache.syncope.core.provisioning.api.Connector; import org.apache.syncope.core.provisioning.api.job.JobExecutionException; import org.apache.syncope.core.provisioning.api.pushpull.GroupPullResultHandler; +import org.apache.syncope.core.provisioning.api.pushpull.InboundActions; import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile; -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; @@ -77,6 +77,7 @@ public List pull( taskType = TaskType.PULL; try { task = entityFactory.newEntity(PullTask.class); + task.setName(pullTaskTO.getName()); task.setResource(resource); task.setMatchingRule(pullTaskTO.getMatchingRule() == null ? MatchingRule.UPDATE : pullTaskTO.getMatchingRule()); @@ -107,60 +108,68 @@ public List pull( }, () -> LOG.debug("Invalid AnyType {} specified, ignoring...", type))); - profile = new ProvisioningProfile<>(connector, task); - profile.setDryRun(false); - profile.setConflictResolutionAction(ConflictResolutionAction.FIRSTMATCH); - profile.getActions().addAll(getPullActions(pullTaskTO.getActions().stream(). - map(implementationDAO::findById).flatMap(Optional::stream). - toList())); - profile.setExecutor(executor); + profile = new ProvisioningProfile<>( + connector, + taskType, + task, + ConflictResolutionAction.FIRSTMATCH, + getInboundActions(pullTaskTO.getActions().stream(). + map(implementationDAO::findById).flatMap(Optional::stream). + toList()), + executor, + false); - for (PullActions action : profile.getActions()) { + dispatcher = new PullResultHandlerDispatcher(profile, this); + + for (InboundActions action : profile.getActions()) { action.beforeAll(profile); } AnyType anyType = anyTypeDAO.findById(provision.getAnyType()). orElseThrow(() -> new NotFoundException("AnyType" + provision.getAnyType())); - 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); + dispatcher.addHandlerSupplier(provision.getObjectClass(), () -> { + SyncopePullResultHandler handler; + switch (anyType.getKind()) { + case USER: + handler = buildUserHandler(); + break; + + case GROUP: + handler = ghandler; + break; + + case ANY_OBJECT: + default: + handler = buildAnyObjectHandler(); + } + handler.setProfile(profile); + return handler; + }); // execute filtered pull Set matg = new HashSet<>(moreAttrsToGet); profile.getActions().forEach(a -> matg.addAll(a.moreAttrsToGet(profile, provision))); Stream mapItems = Stream.concat( - MappingUtils.getPullItems(provision.getMapping().getItems().stream()), + MappingUtils.getInboundItems(provision.getMapping().getItems().stream()), virSchemaDAO.findByResourceAndAnyType(task.getResource().getKey(), anyType.getKey()).stream(). map(VirSchema::asLinkingMappingItem)); connector.filteredReconciliation( new ObjectClass(provision.getObjectClass()), reconFilterBuilder, - handler, + dispatcher, MappingUtils.buildOperationOptions(mapItems, matg.toArray(String[]::new))); try { - setGroupOwners(ghandler); + setGroupOwners(ghandler, groupDAO, anyTypeDAO, inboundMatcher, profile); } catch (Exception e) { LOG.error("While setting group owners", e); } - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.afterAll(profile); } 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 dd490c21afd..869fad927fb 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 @@ -69,12 +69,16 @@ protected void before( task.setPerformDelete(pushTaskTO.isPerformDelete()); task.setSyncStatus(pushTaskTO.isSyncStatus()); - profile = new ProvisioningProfile<>(connector, task); - profile.setExecutor(executor); - profile.getActions().addAll(getPushActions(pushTaskTO.getActions().stream(). - map(implementationDAO::findById).flatMap(Optional::stream). - toList())); - profile.setConflictResolutionAction(ConflictResolutionAction.FIRSTMATCH); + profile = new ProvisioningProfile<>( + connector, + taskType, + task, + ConflictResolutionAction.FIRSTMATCH, + getPushActions(pushTaskTO.getActions().stream(). + map(implementationDAO::findById).flatMap(Optional::stream). + toList()), + executor, + false); for (PushActions action : profile.getActions()) { action.beforeAll(profile); @@ -92,7 +96,7 @@ public List push( try { before(resource, connector, pushTaskTO, executor); - PushResultHandlerDispatcher dispatcher = new PushResultHandlerDispatcher(profile, this); + dispatcher = new PushResultHandlerDispatcher(profile, this); AnyType anyType = anyTypeDAO.findById(provision.getAnyType()). orElseThrow(() -> new NotFoundException("AnyType" + provision.getAnyType())); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SyncopeResultHandlerDispatcher.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SyncopeResultHandlerDispatcher.java index 981c39a6c1a..6de58cfd310 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SyncopeResultHandlerDispatcher.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SyncopeResultHandlerDispatcher.java @@ -91,17 +91,17 @@ public void addHandlerSupplier(final String key, final Supplier supplier) { } protected RA nonConcurrentHandler(final String key) { - return Optional.ofNullable(handlers.get(key)).orElseGet(() -> { - RA h = suppliers.get(key).get(); - handlers.put(key, h); - return h; - }); + return handlers.computeIfAbsent(key, k -> suppliers.get(k).get()); } protected void submit(final Runnable runnable) { tpte.ifPresent(executor -> futures.add(executor.submit(runnable))); } + public void stop() { + handlers.values().forEach(SyncopeResultHandler::stop); + } + protected void shutdown() { for (Future f : this.futures) { try { diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/CSVStreamConnector.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/CSVStreamConnector.java index 5b13469eca9..c9392ddec9d 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/CSVStreamConnector.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/stream/CSVStreamConnector.java @@ -44,6 +44,7 @@ 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.LiveSyncResultsHandler; import org.identityconnectors.framework.common.objects.ObjectClass; import org.identityconnectors.framework.common.objects.ObjectClassInfo; import org.identityconnectors.framework.common.objects.OperationOptions; @@ -240,6 +241,15 @@ public SyncToken getLatestSyncToken(final ObjectClass objectClass) { throw new UnsupportedOperationException(); } + @Override + public void livesync( + final ObjectClass objectClass, + final LiveSyncResultsHandler handler, + final OperationOptions options) { + + throw new UnsupportedOperationException(); + } + @Override public ConnectorObject getObject( final ObjectClass objectClass, 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 index 19ba71043ef..6d3a9c1fbf8 100644 --- 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 @@ -41,17 +41,18 @@ import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.Implementation; 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.policy.InboundCorrelationRuleEntity; +import org.apache.syncope.core.persistence.api.entity.policy.InboundPolicy; 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.job.JobExecutionException; import org.apache.syncope.core.provisioning.api.pushpull.GroupPullResultHandler; +import org.apache.syncope.core.provisioning.api.pushpull.InboundActions; import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile; -import org.apache.syncope.core.provisioning.api.pushpull.PullActions; import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullResultHandler; 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.pushpull.PullResultHandlerDispatcher; import org.apache.syncope.core.provisioning.java.utils.MappingUtils; import org.apache.syncope.core.spring.security.SecureRandomUtils; import org.identityconnectors.framework.common.objects.ObjectClass; @@ -65,32 +66,32 @@ public class StreamPullJobDelegate extends PullJobDelegate implements SyncopeStr @Autowired private RealmSearchDAO realmSearchDAO; - private PullPolicy pullPolicy( + private InboundPolicy inboundPolicy( final AnyType anyType, final ConflictResolutionAction conflictResolutionAction, - final String pullCorrelationRule) { + final String inboundCorrelationRule) { - PullCorrelationRuleEntity pullCorrelationRuleEntity = null; - if (pullCorrelationRule != null) { - Implementation impl = implementationDAO.findById(pullCorrelationRule).orElse(null); - if (impl == null || !IdMImplementationType.PULL_CORRELATION_RULE.equals(impl.getType())) { - LOG.debug("Invalid {} {}, ignoring...", Implementation.class.getSimpleName(), pullCorrelationRule); + InboundCorrelationRuleEntity inboundCorrelationRuleEntity = null; + if (inboundCorrelationRule != null) { + Implementation impl = implementationDAO.findById(inboundCorrelationRule).orElse(null); + if (impl == null || !IdMImplementationType.INBOUND_CORRELATION_RULE.equals(impl.getType())) { + LOG.debug("Invalid {} {}, ignoring...", Implementation.class.getSimpleName(), inboundCorrelationRule); } else { - pullCorrelationRuleEntity = entityFactory.newEntity(PullCorrelationRuleEntity.class); - pullCorrelationRuleEntity.setAnyType(anyType); - pullCorrelationRuleEntity.setImplementation(impl); + inboundCorrelationRuleEntity = entityFactory.newEntity(InboundCorrelationRuleEntity.class); + inboundCorrelationRuleEntity.setAnyType(anyType); + inboundCorrelationRuleEntity.setImplementation(impl); } } - PullPolicy pullPolicy = entityFactory.newEntity(PullPolicy.class); - pullPolicy.setConflictResolutionAction(conflictResolutionAction); + InboundPolicy inboundPolicy = entityFactory.newEntity(InboundPolicy.class); + inboundPolicy.setConflictResolutionAction(conflictResolutionAction); - if (pullCorrelationRuleEntity != null) { - pullPolicy.add(pullCorrelationRuleEntity); - pullCorrelationRuleEntity.setPullPolicy(pullPolicy); + if (inboundCorrelationRuleEntity != null) { + inboundPolicy.add(inboundCorrelationRuleEntity); + inboundCorrelationRuleEntity.setInboundPolicy(inboundPolicy); } - return pullPolicy; + return inboundPolicy; } private Provision provision( @@ -137,7 +138,7 @@ private ExternalResource externalResource( final String keyColumn, final List columns, final ConflictResolutionAction conflictResolutionAction, - final String pullCorrelationRule) throws JobExecutionException { + final String inboundCorrelationRule) throws JobExecutionException { Provision provision = provision(anyType, keyColumn, columns); @@ -145,7 +146,7 @@ private ExternalResource externalResource( resource.setKey("StreamPull_" + SecureRandomUtils.generateRandomUUID().toString()); resource.getProvisions().add(provision); - resource.setPullPolicy(pullPolicy(anyType, conflictResolutionAction, pullCorrelationRule)); + resource.setInboundPolicy(inboundPolicy(anyType, conflictResolutionAction, inboundCorrelationRule)); return resource; } @@ -156,7 +157,7 @@ public List pull( final String keyColumn, final List columns, final ConflictResolutionAction conflictResolutionAction, - final String pullCorrelationRule, + final String inboundCorrelationRule, final Connector connector, final PullTaskTO pullTaskTO, final String executor) throws JobExecutionException { @@ -166,10 +167,11 @@ public List pull( taskType = TaskType.PULL; try { ExternalResource resource = - externalResource(anyType, keyColumn, columns, conflictResolutionAction, pullCorrelationRule); + externalResource(anyType, keyColumn, columns, conflictResolutionAction, inboundCorrelationRule); Provision provision = resource.getProvisions().get(0); task = entityFactory.newEntity(PullTask.class); + task.setName(pullTaskTO.getName()); task.setResource(resource); task.setMatchingRule(pullTaskTO.getMatchingRule()); task.setUnmatchingRule(pullTaskTO.getUnmatchingRule()); @@ -182,56 +184,64 @@ public List pull( orElseThrow(() -> new NotFoundException("Realm " + pullTaskTO.getDestinationRealm()))); task.setRemediation(pullTaskTO.isRemediation()); - profile = new ProvisioningProfile<>(connector, task); - profile.setDryRun(false); - profile.setConflictResolutionAction(conflictResolutionAction); - profile.getActions().addAll(getPullActions(pullTaskTO.getActions().stream(). - map(implementationDAO::findById).flatMap(Optional::stream). - toList())); - profile.setExecutor(executor); + profile = new ProvisioningProfile<>( + connector, + taskType, + task, + conflictResolutionAction, + getInboundActions(pullTaskTO.getActions().stream(). + map(implementationDAO::findById).flatMap(Optional::stream). + toList()), + executor, + false); - for (PullActions action : profile.getActions()) { + dispatcher = new PullResultHandlerDispatcher(profile, this); + + for (InboundActions action : profile.getActions()) { 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); + dispatcher.addHandlerSupplier(provision.getObjectClass(), () -> { + SyncopePullResultHandler handler; + switch (anyType.getKind()) { + case USER: + handler = buildUserHandler(); + break; + + case GROUP: + handler = ghandler; + break; + + case ANY_OBJECT: + default: + handler = buildAnyObjectHandler(); + } + handler.setProfile(profile); + return handler; + }); // execute filtered pull Set moreAttrsToGet = new HashSet<>(); profile.getActions().forEach(a -> moreAttrsToGet.addAll(a.moreAttrsToGet(profile, provision))); Stream mapItems = Stream.concat( - MappingUtils.getPullItems(provision.getMapping().getItems().stream()), + MappingUtils.getInboundItems(provision.getMapping().getItems().stream()), virSchemaDAO.findByResourceAndAnyType(resource.getKey(), anyType.getKey()).stream(). map(VirSchema::asLinkingMappingItem)); connector.fullReconciliation( new ObjectClass(provision.getObjectClass()), - handler, + dispatcher, MappingUtils.buildOperationOptions(mapItems, moreAttrsToGet.toArray(String[]::new))); try { - setGroupOwners(ghandler); + setGroupOwners(ghandler, groupDAO, anyTypeDAO, inboundMatcher, profile); } catch (Exception e) { LOG.error("While setting group owners", e); } - for (PullActions action : profile.getActions()) { + for (InboundActions action : profile.getActions()) { action.afterAll(profile); } 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 index ee33c89f7a0..76eec0d9d06 100644 --- 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 @@ -138,14 +138,18 @@ public List push( task.setPerformDelete(true); task.setSyncStatus(false); - profile = new ProvisioningProfile<>(connector, task); - profile.setExecutor(executor); - profile.getActions().addAll(getPushActions(pushTaskTO.getActions().stream(). - map(implementationDAO::findById).flatMap(Optional::stream). - toList())); - profile.setConflictResolutionAction(ConflictResolutionAction.FIRSTMATCH); - - PushResultHandlerDispatcher dispatcher = new PushResultHandlerDispatcher(profile, this); + profile = new ProvisioningProfile<>( + connector, + taskType, + task, + ConflictResolutionAction.FIRSTMATCH, + getPushActions(pushTaskTO.getActions().stream(). + map(implementationDAO::findById).flatMap(Optional::stream). + toList()), + executor, + false); + + dispatcher = new PushResultHandlerDispatcher(profile, this); for (PushActions action : profile.getActions()) { action.beforeAll(profile); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java index 8b73e7e07a0..42386aeadeb 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java @@ -46,7 +46,7 @@ import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy; -import org.apache.syncope.core.persistence.api.entity.task.PullTask; +import org.apache.syncope.core.persistence.api.entity.task.InboundTask; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.provisioning.api.MappingManager; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; @@ -171,7 +171,7 @@ public ConnObjectUtils( * Build a UserCR / GroupCR / AnyObjectCR out of connector object attributes and schema mapping. * * @param obj connector object - * @param pullTask pull task + * @param inboundTask inbound task * @param anyTypeKind any type kind * @param provision provision information * @param generatePassword whether password value shall be generated, in case not found from @@ -182,12 +182,12 @@ public ConnObjectUtils( @Transactional(readOnly = true) public C getAnyCR( final ConnectorObject obj, - final PullTask pullTask, + final InboundTask inboundTask, final AnyTypeKind anyTypeKind, final Provision provision, final boolean generatePassword) { - AnyTO anyTO = getAnyTOFromConnObject(obj, pullTask, anyTypeKind, provision); + AnyTO anyTO = getAnyTOFromConnObject(obj, inboundTask, anyTypeKind, provision); C anyCR = anyUtilsFactory.getInstance(anyTypeKind).newAnyCR(); EntityTOUtils.toAnyCR(anyTO, anyCR); @@ -221,7 +221,7 @@ public C getAnyCR( public RealmTO getRealmTO(final ConnectorObject obj, final OrgUnit orgUnit) { RealmTO realmTO = new RealmTO(); - MappingUtils.getPullItems(orgUnit.getItems().stream()). + MappingUtils.getInboundItems(orgUnit.getItems().stream()). forEach(item -> mappingManager.setIntValues( item, obj.getAttributeByName(item.getExtAttrName()), realmTO)); @@ -234,7 +234,7 @@ public RealmTO getRealmTO(final ConnectorObject obj, final OrgUnit orgUnit) { * @param key any object to be updated * @param obj connector object * @param original any object to get diff from - * @param pullTask pull task + * @param inboundTask inbound task * @param anyTypeKind any type kind * @param provision provision information * @param any object @@ -246,11 +246,11 @@ public U getAnyUR( final String key, final ConnectorObject obj, final AnyTO original, - final PullTask pullTask, + final InboundTask inboundTask, final AnyTypeKind anyTypeKind, final Provision provision) { - AnyTO updated = getAnyTOFromConnObject(obj, pullTask, anyTypeKind, provision); + AnyTO updated = getAnyTOFromConnObject(obj, inboundTask, anyTypeKind, provision); updated.setKey(key); U anyUR; @@ -320,7 +320,7 @@ public U getAnyUR( protected T getAnyTOFromConnObject( final ConnectorObject obj, - final PullTask pullTask, + final InboundTask inboundTask, final AnyTypeKind anyTypeKind, final Provision provision) { @@ -329,12 +329,13 @@ protected T getAnyTOFromConnObject( anyTO.getAuxClasses().addAll(provision.getAuxClasses()); // 1. fill with data from connector object - anyTO.setRealm(pullTask.getDestinationRealm().getFullPath()); - MappingUtils.getPullItems(provision.getMapping().getItems().stream()).forEach( + anyTO.setRealm(inboundTask.getDestinationRealm().getFullPath()); + MappingUtils.getInboundItems(provision.getMapping().getItems().stream()).forEach( item -> mappingManager.setIntValues(item, obj.getAttributeByName(item.getExtAttrName()), anyTO)); // 2. add data from defined template (if any) - pullTask.getTemplate(provision.getAnyType()).ifPresent(template -> templateUtils.apply(anyTO, template.get())); + inboundTask.getTemplate(provision.getAnyType()). + ifPresent(template -> templateUtils.apply(anyTO, template.get())); return anyTO; } 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 a79162fb06b..d60ad04a928 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 @@ -60,13 +60,11 @@ public static Optional getConnObjectKeyItem(final Provision provision) { } public static Stream getPropagationItems(final Stream items) { - return items.filter( - item -> item.getPurpose() == MappingPurpose.PROPAGATION || item.getPurpose() == MappingPurpose.BOTH); + return items.filter(i -> i.getPurpose() == MappingPurpose.PROPAGATION || i.getPurpose() == MappingPurpose.BOTH); } - public static Stream getPullItems(final Stream items) { - return items.filter( - item -> item.getPurpose() == MappingPurpose.PULL || item.getPurpose() == MappingPurpose.BOTH); + public static Stream getInboundItems(final Stream items) { + return items.filter(i -> i.getPurpose() == MappingPurpose.PULL || i.getPurpose() == MappingPurpose.BOTH); } public static List getItemTransformers( diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DummyImplementationLookup.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DummyImplementationLookup.java index 8bb73697a69..fe63dd5e89e 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DummyImplementationLookup.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DummyImplementationLookup.java @@ -20,17 +20,17 @@ import java.util.Set; import org.apache.syncope.common.lib.policy.AccountRuleConf; +import org.apache.syncope.common.lib.policy.InboundCorrelationRuleConf; 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.ReportConf; import org.apache.syncope.core.provisioning.api.ImplementationLookup; import org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate; import org.apache.syncope.core.provisioning.api.rules.AccountRule; +import org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRule; import org.apache.syncope.core.provisioning.api.rules.PasswordRule; -import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRule; import org.apache.syncope.core.provisioning.api.rules.PushCorrelationRule; -import org.apache.syncope.core.provisioning.java.pushpull.DefaultPullCorrelationRule; +import org.apache.syncope.core.provisioning.java.pushpull.DefaultInboundCorrelationRule; import org.apache.syncope.core.provisioning.java.pushpull.DefaultPushCorrelationRule; import org.apache.syncope.core.spring.policy.DefaultAccountRule; import org.apache.syncope.core.spring.policy.DefaultPasswordRule; @@ -67,10 +67,10 @@ public Class getPasswordRuleClass( } @Override - public Class getPullCorrelationRuleClass( - final Class pullCorrelationRuleConfClass) { + public Class getInboundCorrelationRuleClass( + final Class inboundCorrelationRuleConfClass) { - return DefaultPullCorrelationRule.class; + return DefaultInboundCorrelationRule.class; } @Override diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/DBPasswordPullActionsTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/DBPasswordPullActionsTest.java index 540718c2a27..11577fc0038 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/DBPasswordPullActionsTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/DBPasswordPullActionsTest.java @@ -26,9 +26,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.util.HashSet; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; -import java.util.Set; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.request.PasswordPatch; import org.apache.syncope.common.lib.request.UserCR; @@ -75,7 +75,7 @@ public class DBPasswordPullActionsTest extends AbstractTest { @Mock private ConnInstance connInstance; - private Set connConfProperties; + private List connConfProperties; private UserTO userTO; @@ -98,7 +98,7 @@ public void initTest() { connConfPropSchema.setName("cipherAlgorithm"); connConfProperty = new ConnConfProperty(); connConfProperty.setSchema(connConfPropSchema); - connConfProperties = new HashSet<>(); + connConfProperties = new ArrayList<>(); connConfProperties.add(connConfProperty); ReflectionTestUtils.setField(dBPasswordPullActions, "encodedPassword", encodedPassword); diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActionsTest.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActionsTest.java index baf40438423..a5852e59a06 100644 --- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActionsTest.java +++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/pushpull/LDAPMembershipPullActionsTest.java @@ -58,7 +58,7 @@ import org.apache.syncope.core.provisioning.api.Connector; import org.apache.syncope.core.provisioning.api.job.JobExecutionException; import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile; -import org.apache.syncope.core.provisioning.api.rules.PullMatch; +import org.apache.syncope.core.provisioning.api.rules.InboundMatch; import org.apache.syncope.core.provisioning.java.AbstractTest; import org.identityconnectors.framework.common.objects.ConnectorObject; import org.identityconnectors.framework.common.objects.SyncDelta; @@ -124,7 +124,7 @@ public class LDAPMembershipPullActionsTest extends AbstractTest { private User user; - private Set connConfProperties; + private List connConfProperties; @BeforeEach public void initTest() { @@ -148,7 +148,7 @@ public void initTest() { connConfPropSchema.setName("testSchemaName"); ConnConfProperty connConfProperty = new ConnConfProperty(); connConfProperty.setSchema(connConfPropSchema); - connConfProperties = Set.of(connConfProperty); + connConfProperties = List.of(connConfProperty); lenient().when(profile.getTask()).thenAnswer(ic -> pullTask); lenient().when(pullTask.getResource()).thenReturn(resource); @@ -198,7 +198,7 @@ public void after() throws JobExecutionException { when(connectorObj.getAttributeByName(anyString())).thenReturn(new Uid(UUID.randomUUID().toString())); when(inboundMatcher.match(any(AnyType.class), anyString(), any(ExternalResource.class), any(Connector.class))). - thenReturn(Optional.of(new PullMatch(MatchType.ANY, user))); + thenReturn(Optional.of(new InboundMatch(MatchType.ANY, user))); ldapMembershipPullActions.after(profile, syncDelta, entity, result); diff --git a/core/self-keymaster-starter/pom.xml b/core/self-keymaster-starter/pom.xml index 6957e32254a..ed2bf49116a 100644 --- a/core/self-keymaster-starter/pom.xml +++ b/core/self-keymaster-starter/pom.xml @@ -102,11 +102,6 @@ under the License. - - org.springframework.boot - spring-boot-starter-undertow - - org.postgresql postgresql diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/implementation/ImplementationManager.java b/core/spring/src/main/java/org/apache/syncope/core/spring/implementation/ImplementationManager.java index 95b2b966821..3a8beecac19 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/implementation/ImplementationManager.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/implementation/ImplementationManager.java @@ -31,8 +31,8 @@ import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.common.lib.command.CommandArgs; import org.apache.syncope.common.lib.policy.AccountRuleConf; +import org.apache.syncope.common.lib.policy.InboundCorrelationRuleConf; 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.ReportConf; import org.apache.syncope.common.lib.types.IdRepoImplementationType; @@ -41,8 +41,8 @@ import org.apache.syncope.core.provisioning.api.ImplementationLookup; import org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate; import org.apache.syncope.core.provisioning.api.rules.AccountRule; +import org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRule; import org.apache.syncope.core.provisioning.api.rules.PasswordRule; -import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRule; import org.apache.syncope.core.provisioning.api.rules.PushCorrelationRule; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; import org.apache.syncope.core.spring.ApplicationContextProvider; @@ -136,10 +136,10 @@ public static Optional buildPasswordRule( } @SuppressWarnings("unchecked") - public static Optional buildPullCorrelationRule( + public static Optional buildInboundCorrelationRule( final Implementation impl, - final Supplier cacheGetter, - final Consumer cachePutter) + final Supplier cacheGetter, + final Consumer cachePutter) throws ClassNotFoundException { switch (impl.getEngine()) { @@ -148,16 +148,16 @@ public static Optional buildPullCorrelationRule( case JAVA: default: - PullCorrelationRuleConf conf = POJOHelper.deserialize(impl.getBody(), PullCorrelationRuleConf.class); - Class clazz = - (Class) ApplicationContextProvider.getApplicationContext(). - getBean(ImplementationLookup.class).getPullCorrelationRuleClass(conf.getClass()); - + InboundCorrelationRuleConf conf = POJOHelper.deserialize( + impl.getBody(), InboundCorrelationRuleConf.class); + Class clazz = + (Class) ApplicationContextProvider.getApplicationContext(). + getBean(ImplementationLookup.class).getInboundCorrelationRuleClass(conf.getClass()); if (clazz == null) { return Optional.empty(); } - PullCorrelationRule rule = build(clazz, true, cacheGetter, cachePutter); + InboundCorrelationRule rule = build(clazz, true, cacheGetter, cachePutter); rule.setConf(conf); return Optional.of(rule); } diff --git a/core/spring/src/test/java/org/apache/syncope/core/spring/security/DummyImplementationLookup.java b/core/spring/src/test/java/org/apache/syncope/core/spring/security/DummyImplementationLookup.java index ae67678cc0e..e1c7fedca92 100644 --- a/core/spring/src/test/java/org/apache/syncope/core/spring/security/DummyImplementationLookup.java +++ b/core/spring/src/test/java/org/apache/syncope/core/spring/security/DummyImplementationLookup.java @@ -20,15 +20,15 @@ import java.util.Set; import org.apache.syncope.common.lib.policy.AccountRuleConf; +import org.apache.syncope.common.lib.policy.InboundCorrelationRuleConf; 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.ReportConf; import org.apache.syncope.core.provisioning.api.ImplementationLookup; import org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate; import org.apache.syncope.core.provisioning.api.rules.AccountRule; +import org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRule; import org.apache.syncope.core.provisioning.api.rules.PasswordRule; -import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRule; import org.apache.syncope.core.provisioning.api.rules.PushCorrelationRule; public class DummyImplementationLookup implements ImplementationLookup { @@ -63,8 +63,8 @@ public Class getPasswordRuleClass( } @Override - public Class getPullCorrelationRuleClass( - final Class pullCorrelationRuleConfClass) { + public Class getInboundCorrelationRuleClass( + final Class inboundCorrelationRuleConfClass) { return null; } diff --git a/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/ExternalResourcesHealthIndicator.java b/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/ExternalResourcesHealthIndicator.java index 3058189b8b5..17c58feb96c 100644 --- a/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/ExternalResourcesHealthIndicator.java +++ b/core/starter/src/main/java/org/apache/syncope/core/starter/actuate/ExternalResourcesHealthIndicator.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.starter.actuate; -import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Stream; import org.apache.syncope.common.keymaster.client.api.DomainOps; @@ -74,8 +73,7 @@ public Health health() { connectorManager.buildConnInstanceOverride( connInstanceDataBinder.getConnInstanceTO(resource.getConnector()), resource.getConfOverride(), - resource.isOverrideCapabilities() - ? Optional.of(resource.getCapabilitiesOverride()) : Optional.empty())). + resource.getCapabilitiesOverride())). test(); status = Status.UP; } catch (Exception e) { diff --git a/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/DummyImplementationLookup.java b/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/DummyImplementationLookup.java index a43e9e95cfb..c8bb5201aa7 100644 --- a/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/DummyImplementationLookup.java +++ b/core/workflow-java/src/test/java/org/apache/syncope/core/workflow/java/DummyImplementationLookup.java @@ -20,15 +20,15 @@ import java.util.Set; import org.apache.syncope.common.lib.policy.AccountRuleConf; +import org.apache.syncope.common.lib.policy.InboundCorrelationRuleConf; 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.ReportConf; import org.apache.syncope.core.provisioning.api.ImplementationLookup; import org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate; import org.apache.syncope.core.provisioning.api.rules.AccountRule; +import org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRule; import org.apache.syncope.core.provisioning.api.rules.PasswordRule; -import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRule; import org.apache.syncope.core.provisioning.api.rules.PushCorrelationRule; import org.apache.syncope.core.spring.policy.DefaultAccountRule; import org.apache.syncope.core.spring.policy.DefaultPasswordRule; @@ -65,8 +65,8 @@ public Class getPasswordRuleClass( } @Override - public Class getPullCorrelationRuleClass( - final Class pullCorrelationRuleConfClass) { + public Class getInboundCorrelationRuleClass( + final Class inboundCorrelationRuleConfClass) { return null; } diff --git a/docker/console/pom.xml b/docker/console/pom.xml index ffc8688316f..11a332e1a12 100644 --- a/docker/console/pom.xml +++ b/docker/console/pom.xml @@ -38,11 +38,6 @@ under the License. - - org.springframework.boot - spring-boot-starter-undertow - - org.apache.syncope.client.idm syncope-client-idm-console diff --git a/docker/core/pom.xml b/docker/core/pom.xml index 2a0f0d5425d..8438992b4d2 100644 --- a/docker/core/pom.xml +++ b/docker/core/pom.xml @@ -38,11 +38,6 @@ under the License. - - org.springframework.boot - spring-boot-starter-undertow - - org.apache.syncope.core syncope-core-starter diff --git a/docker/enduser/pom.xml b/docker/enduser/pom.xml index ca60486ebf1..2ea5e77d109 100644 --- a/docker/enduser/pom.xml +++ b/docker/enduser/pom.xml @@ -38,11 +38,6 @@ under the License. - - org.springframework.boot - spring-boot-starter-undertow - - org.apache.syncope.client.idrepo syncope-client-idrepo-enduser diff --git a/docker/src/main/resources/docker-compose/docker-compose-all.yml b/docker/src/main/resources/docker-compose/docker-compose-all.yml index fe995fcf342..ce63fe1bb74 100644 --- a/docker/src/main/resources/docker-compose/docker-compose-all.yml +++ b/docker/src/main/resources/docker-compose/docker-compose-all.yml @@ -22,7 +22,7 @@ services: keymaster: - image: zookeeper:3.9.2 + image: zookeeper:3.9.3 restart: always db: diff --git a/docker/wa/pom.xml b/docker/wa/pom.xml index 53492951964..97a9d07ed62 100644 --- a/docker/wa/pom.xml +++ b/docker/wa/pom.xml @@ -50,11 +50,6 @@ under the License. - - org.springframework.boot - spring-boot-starter-undertow - - org.apache.syncope.wa syncope-wa-starter diff --git a/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java b/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java index 38fd6031ade..9535b55d865 100644 --- a/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java +++ b/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java @@ -157,10 +157,8 @@ protected TypeMapping auditMapping() { } @Override - protected String doExecute(final boolean dryRun, final String executor, final JobExecutionContext context) - throws JobExecutionException { - - if (!dryRun) { + protected String doExecute(final JobExecutionContext context) throws JobExecutionException { + if (!context.isDryRun()) { setStatus("Start rebuilding indexes"); try { diff --git a/ext/opensearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/OpenSearchReindex.java b/ext/opensearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/OpenSearchReindex.java index 693c492076a..cdd7359a51b 100644 --- a/ext/opensearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/OpenSearchReindex.java +++ b/ext/opensearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/OpenSearchReindex.java @@ -112,10 +112,8 @@ protected TypeMapping auditMapping() { } @Override - protected String doExecute(final boolean dryRun, final String executor, final JobExecutionContext context) - throws JobExecutionException { - - if (!dryRun) { + protected String doExecute(final JobExecutionContext context) throws JobExecutionException { + if (!context.isDryRun()) { setStatus("Start rebuilding indexes"); try { diff --git a/fit/build-tools/pom.xml b/fit/build-tools/pom.xml index da12044ed76..d8b018ee12a 100644 --- a/fit/build-tools/pom.xml +++ b/fit/build-tools/pom.xml @@ -52,6 +52,10 @@ under the License. org.apache.tomcat.embed tomcat-embed-el + + org.springframework.boot + spring-boot-starter-tomcat + @@ -77,7 +81,6 @@ under the License. org.apache.cxf cxf-rt-transports-http - ${cxf.version} org.apache.cxf @@ -92,11 +95,17 @@ under the License. org.apache.cxf cxf-rt-rs-service-description + com.fasterxml.jackson.jakarta.rs jackson-jakarta-rs-json-provider + + org.springframework.kafka + spring-kafka-test + + net.tirasa.connid.bundles.soap soap-utilities @@ -359,7 +368,7 @@ under the License. true - wildfly31x + wildfly33x https://github.com/wildfly/wildfly/releases/download/${wildfly.version}/wildfly-${wildfly.version}.zip ${settings.localRepository}/org/codehaus/cargo/cargo-container-archives diff --git a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/H2StartStopListener.java b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/H2StartStopListener.java index 1be8688f4b9..347ec9e4e96 100644 --- a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/H2StartStopListener.java +++ b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/H2StartStopListener.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.fit.buildtools; -import jakarta.servlet.ServletContext; import jakarta.servlet.ServletContextEvent; import jakarta.servlet.ServletContextListener; import jakarta.servlet.annotation.WebListener; @@ -42,19 +41,16 @@ public class H2StartStopListener implements ServletContextListener { private static final Logger LOG = LoggerFactory.getLogger(H2StartStopListener.class); - private static final String H2_TESTDB = "h2TestDb"; + private Server h2TestDb; @Override public void contextInitialized(final ServletContextEvent sce) { - ServletContext context = sce.getServletContext(); - WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(context); + WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext()); try { - Server h2TestDb = new Server(); + h2TestDb = new Server(); h2TestDb.runTool("-ifNotExists", "-tcp", "-tcpDaemon", "-web", "-webDaemon", "-webPort", Objects.requireNonNull(ctx).getEnvironment().getProperty("testdb.webport")); - - context.setAttribute(H2_TESTDB, h2TestDb); } catch (SQLException e) { LOG.error("Could not start H2 test db", e); } @@ -78,7 +74,6 @@ public void contextInitialized(final ServletContextEvent sce) { @Override public void contextDestroyed(final ServletContextEvent sce) { - Server h2TestDb = (Server) sce.getServletContext().getAttribute(H2_TESTDB); if (h2TestDb != null) { h2TestDb.shutdown(); } diff --git a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/KafkaBrokerStartStopListener.java b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/KafkaBrokerStartStopListener.java new file mode 100644 index 00000000000..985ba9b3e16 --- /dev/null +++ b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/KafkaBrokerStartStopListener.java @@ -0,0 +1,60 @@ +/* + * 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.fit.buildtools; + +import jakarta.servlet.ServletContextEvent; +import jakarta.servlet.ServletContextListener; +import jakarta.servlet.annotation.WebListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.kafka.test.EmbeddedKafkaZKBroker; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.context.support.WebApplicationContextUtils; + +@WebListener +public class KafkaBrokerStartStopListener implements ServletContextListener { + + private static final Logger LOG = LoggerFactory.getLogger(KafkaBrokerStartStopListener.class); + + private EmbeddedKafkaZKBroker embeddedKafkaBroker; + + @Override + public void contextInitialized(final ServletContextEvent sce) { + WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext()); + + embeddedKafkaBroker = new EmbeddedKafkaZKBroker( + 1, + false, + ctx.getEnvironment().getProperty("kafka.topics", String[].class)). + kafkaPorts(ctx.getEnvironment().getProperty("kafka.port", Integer.class)); + + embeddedKafkaBroker.afterPropertiesSet(); + + LOG.info("Kafka broker successfully (re)started"); + } + + @Override + public void contextDestroyed(final ServletContextEvent sce) { + if (embeddedKafkaBroker != null) { + embeddedKafkaBroker.destroy(); + + LOG.info("Kafka broker successfully stopped"); + } + } +} diff --git a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/SyncopeBuildToolsApplication.java b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/SyncopeBuildToolsApplication.java index ec9c0671d34..72d42eff1fa 100644 --- a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/SyncopeBuildToolsApplication.java +++ b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/SyncopeBuildToolsApplication.java @@ -121,6 +121,7 @@ public void onStartup(final ServletContext sc) throws ServletException { sc.addListener(new LDAPStartStopListener()); sc.addListener(new H2StartStopListener()); sc.addListener(new GreenMailStartStopListener()); + sc.addListener(new KafkaBrokerStartStopListener()); ServletRegistration.Dynamic sts = sc.addServlet("ServiceTimeoutServlet", ServiceTimeoutServlet.class); sts.addMapping("/services/*"); diff --git a/fit/build-tools/src/main/resources/application.properties b/fit/build-tools/src/main/resources/application.properties index 4dd3aff4572..579a6fc1e19 100644 --- a/fit/build-tools/src/main/resources/application.properties +++ b/fit/build-tools/src/main/resources/application.properties @@ -23,6 +23,9 @@ server.servlet.encoding.charset=UTF-8 server.servlet.encoding.enabled=true server.servlet.encoding.force=true +testconnectorserver.port=${testconnectorserver.port} +testconnectorserver.key=${testconnectorserver.key} + server.servlet.contextPath=/syncope-fit-build-tools cxf.path=/cxf @@ -37,8 +40,8 @@ testds.bindDn=${testds.bindDn} testds.password=${testds.password} testds.port=${testds.port} -testconnectorserver.port=${testconnectorserver.port} -testconnectorserver.key=${testconnectorserver.key} - testmail.smtpport=${testmail.smtpport} testmail.pop3port=${testmail.pop3port} + +kafka.port=${testkafka.port} +kafka.topics=${testkafka.topics} diff --git a/fit/build-tools/src/main/resources/log4j2.xml b/fit/build-tools/src/main/resources/log4j2.xml index 7551796b740..759f855758e 100644 --- a/fit/build-tools/src/main/resources/log4j2.xml +++ b/fit/build-tools/src/main/resources/log4j2.xml @@ -32,6 +32,13 @@ under the License. + + + + + + + diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml index 97a2dc76373..28112d94395 100644 --- a/fit/core-reference/pom.xml +++ b/fit/core-reference/pom.xml @@ -196,6 +196,11 @@ under the License. h2 test + + org.apache.kafka + kafka-clients + test + org.apache.wicket wicket-tester @@ -1275,6 +1280,10 @@ under the License. org.apache.tomcat.embed tomcat-embed-el + + org.springframework.boot + spring-boot-starter-tomcat + @@ -1307,6 +1316,19 @@ under the License. org.postgresql postgresql + + + org.apache.syncope.client.idrepo + syncope-client-idrepo-enduser + ${project.version} + + + org.springframework.boot + spring-boot-starter-tomcat + + + test + diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DummyPullCorrelationRule.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DummyInboundCorrelationRule.java similarity index 68% rename from fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DummyPullCorrelationRule.java rename to fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DummyInboundCorrelationRule.java index c712ed57dcc..78a728120a8 100644 --- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DummyPullCorrelationRule.java +++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DummyInboundCorrelationRule.java @@ -20,15 +20,15 @@ import org.apache.syncope.common.lib.to.Provision; import org.apache.syncope.core.persistence.api.dao.search.SearchCond; -import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRule; -import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRuleConfClass; -import org.identityconnectors.framework.common.objects.SyncDelta; +import org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRule; +import org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRuleConfClass; +import org.identityconnectors.framework.common.objects.LiveSyncDelta; -@PullCorrelationRuleConfClass(DummyPullCorrelationRuleConf.class) -public class DummyPullCorrelationRule implements PullCorrelationRule { +@InboundCorrelationRuleConfClass(DummyInboundCorrelationRuleConf.class) +public class DummyInboundCorrelationRule implements InboundCorrelationRule { @Override - public SearchCond getSearchCond(final SyncDelta syncDelta, final Provision provision) { + public SearchCond getSearchCond(final LiveSyncDelta syncDelta, final Provision provision) { return new SearchCond(); } } diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DummyPullCorrelationRuleConf.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DummyInboundCorrelationRuleConf.java similarity index 83% rename from fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DummyPullCorrelationRuleConf.java rename to fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DummyInboundCorrelationRuleConf.java index 2c869f391d1..7e79603e765 100644 --- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DummyPullCorrelationRuleConf.java +++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DummyInboundCorrelationRuleConf.java @@ -19,10 +19,10 @@ package org.apache.syncope.fit.core.reference; import org.apache.syncope.common.lib.policy.AbstractCorrelationRuleConf; -import org.apache.syncope.common.lib.policy.PullCorrelationRuleConf; +import org.apache.syncope.common.lib.policy.InboundCorrelationRuleConf; -public class DummyPullCorrelationRuleConf - extends AbstractCorrelationRuleConf implements PullCorrelationRuleConf { +public class DummyInboundCorrelationRuleConf + extends AbstractCorrelationRuleConf implements InboundCorrelationRuleConf { private static final long serialVersionUID = -2984203196323732531L; diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java index 2339a88e8c5..a091e1b9956 100644 --- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java +++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java @@ -26,12 +26,12 @@ import javax.sql.DataSource; import org.apache.syncope.common.lib.policy.AccountRuleConf; import org.apache.syncope.common.lib.policy.DefaultAccountRuleConf; +import org.apache.syncope.common.lib.policy.DefaultInboundCorrelationRuleConf; import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf; -import org.apache.syncope.common.lib.policy.DefaultPullCorrelationRuleConf; import org.apache.syncope.common.lib.policy.DefaultPushCorrelationRuleConf; import org.apache.syncope.common.lib.policy.HaveIBeenPwnedPasswordRuleConf; +import org.apache.syncope.common.lib.policy.InboundCorrelationRuleConf; 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.ReportConf; import org.apache.syncope.common.lib.types.IdMImplementationType; @@ -43,8 +43,8 @@ import org.apache.syncope.core.provisioning.api.ImplementationLookup; import org.apache.syncope.core.provisioning.api.job.report.ReportJobDelegate; import org.apache.syncope.core.provisioning.api.rules.AccountRule; +import org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRule; import org.apache.syncope.core.provisioning.api.rules.PasswordRule; -import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRule; import org.apache.syncope.core.provisioning.api.rules.PushCorrelationRule; import org.apache.syncope.core.provisioning.java.job.ExpiredAccessTokenCleanup; import org.apache.syncope.core.provisioning.java.job.ExpiredBatchCleanup; @@ -55,11 +55,12 @@ import org.apache.syncope.core.provisioning.java.propagation.LDAPMembershipPropagationActions; import org.apache.syncope.core.provisioning.java.propagation.LDAPPasswordPropagationActions; import org.apache.syncope.core.provisioning.java.pushpull.DBPasswordPullActions; +import org.apache.syncope.core.provisioning.java.pushpull.DefaultInboundCorrelationRule; import org.apache.syncope.core.provisioning.java.pushpull.DefaultProvisionSorter; -import org.apache.syncope.core.provisioning.java.pushpull.DefaultPullCorrelationRule; import org.apache.syncope.core.provisioning.java.pushpull.DefaultPushCorrelationRule; import org.apache.syncope.core.provisioning.java.pushpull.LDAPMembershipPullActions; import org.apache.syncope.core.provisioning.java.pushpull.LDAPPasswordPullActions; +import org.apache.syncope.core.provisioning.java.pushpull.LiveSyncJobDelegate; import org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate; import org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate; import org.apache.syncope.core.spring.policy.DefaultAccountRule; @@ -92,11 +93,13 @@ public class ITImplementationLookup implements ImplementationLookup { HaveIBeenPwnedPasswordRuleConf.class, HaveIBeenPwnedPasswordRule.class); private static final Map< - Class, Class> PULL_CR_CLASSES = - Map.of( - DummyPullCorrelationRuleConf.class, DummyPullCorrelationRule.class, - DefaultPullCorrelationRuleConf.class, DefaultPullCorrelationRule.class, - LinkedAccountSamplePullCorrelationRuleConf.class, LinkedAccountSamplePullCorrelationRule.class); + Class, Class> INBOUND_CR_CLASSES = + Map.of(DummyInboundCorrelationRuleConf.class, + DummyInboundCorrelationRule.class, + DefaultInboundCorrelationRuleConf.class, + DefaultInboundCorrelationRule.class, + LinkedAccountSampleInboundCorrelationRuleConf.class, + LinkedAccountSampleInboundCorrelationRule.class); private static final Map< Class, Class> PUSH_CR_CLASSES = @@ -104,11 +107,9 @@ public class ITImplementationLookup implements ImplementationLookup { DummyPushCorrelationRuleConf.class, DummyPushCorrelationRule.class, DefaultPushCorrelationRuleConf.class, DefaultPushCorrelationRule.class); - private static final Set> PROVISION_SORTER_CLASSES = - Set.of(DefaultProvisionSorter.class); + private static final Set> PROVISION_SORTER_CLASSES = Set.of(DefaultProvisionSorter.class); - private static final Set> COMMAND_CLASSES = - Set.of(TestCommand.class); + private static final Set> COMMAND_CLASSES = Set.of(TestCommand.class); private static final Map> CLASS_NAMES = new HashMap<>() { @@ -133,19 +134,25 @@ public class ITImplementationLookup implements ImplementationLookup { put(IdRepoImplementationType.ITEM_TRANSFORMER, classNames); classNames = new HashSet<>(); - classNames.add(MacroJobDelegate.class.getName()); - classNames.add(PullJobDelegate.class.getName()); - classNames.add(PushJobDelegate.class.getName()); classNames.add(ExpiredAccessTokenCleanup.class.getName()); classNames.add(ExpiredBatchCleanup.class.getName()); classNames.add(TestSampleJobDelegate.class.getName()); + classNames.add(MacroJobDelegate.class.getName()); + classNames.add(LiveSyncJobDelegate.class.getName()); + classNames.add(PullJobDelegate.class.getName()); + classNames.add(PushJobDelegate.class.getName()); put(IdRepoImplementationType.TASKJOB_DELEGATE, classNames); classNames = new HashSet<>(); put(IdMImplementationType.RECON_FILTER_BUILDER, classNames); + classNames = new HashSet<>(); + classNames.add(TestLiveSyncDeltaMapper.class.getName()); + put(IdMImplementationType.LIVE_SYNC_DELTA_MAPPER, classNames); + classNames = new HashSet<>(); put(IdRepoImplementationType.LOGIC_ACTIONS, classNames); + classNames = new HashSet<>(); classNames.add(TestMacroActions.class.getName()); put(IdRepoImplementationType.MACRO_ACTIONS, classNames); @@ -160,17 +167,17 @@ public class ITImplementationLookup implements ImplementationLookup { classNames = new HashSet<>(); classNames.add(LDAPPasswordPullActions.class.getName()); - classNames.add(TestPullActions.class.getName()); + classNames.add(TestInboundActions.class.getName()); classNames.add(LDAPMembershipPullActions.class.getName()); classNames.add(DBPasswordPullActions.class.getName()); - put(IdMImplementationType.PULL_ACTIONS, classNames); + put(IdMImplementationType.INBOUND_ACTIONS, classNames); classNames = new HashSet<>(); put(IdMImplementationType.PUSH_ACTIONS, classNames); classNames = new HashSet<>(); - classNames.add(DummyPullCorrelationRule.class.getName()); - put(IdMImplementationType.PULL_CORRELATION_RULE, classNames); + classNames.add(DummyInboundCorrelationRule.class.getName()); + put(IdMImplementationType.INBOUND_CORRELATION_RULE, classNames); classNames = new HashSet<>(); classNames.add(DummyPushCorrelationRule.class.getName()); @@ -256,10 +263,10 @@ public Class getPasswordRuleClass( } @Override - public Class getPullCorrelationRuleClass( - final Class pullCorrelationRuleConfClass) { + public Class getInboundCorrelationRuleClass( + final Class inboundCorrelationRuleConfClass) { - return PULL_CR_CLASSES.get(pullCorrelationRuleConfClass); + return INBOUND_CR_CLASSES.get(inboundCorrelationRuleConfClass); } @Override diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/LinkedAccountSamplePullCorrelationRule.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/LinkedAccountSampleInboundCorrelationRule.java similarity index 72% rename from fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/LinkedAccountSamplePullCorrelationRule.java rename to fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/LinkedAccountSampleInboundCorrelationRule.java index 806ef5806a0..832af71be1f 100644 --- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/LinkedAccountSamplePullCorrelationRule.java +++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/LinkedAccountSampleInboundCorrelationRule.java @@ -25,17 +25,17 @@ import org.apache.syncope.core.persistence.api.dao.search.AttrCond; import org.apache.syncope.core.persistence.api.dao.search.SearchCond; import org.apache.syncope.core.persistence.api.entity.Any; -import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRule; -import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRuleConfClass; -import org.apache.syncope.core.provisioning.api.rules.PullMatch; +import org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRule; +import org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRuleConfClass; +import org.apache.syncope.core.provisioning.api.rules.InboundMatch; import org.identityconnectors.framework.common.objects.Attribute; -import org.identityconnectors.framework.common.objects.SyncDelta; +import org.identityconnectors.framework.common.objects.LiveSyncDelta; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; -@PullCorrelationRuleConfClass(LinkedAccountSamplePullCorrelationRuleConf.class) -public class LinkedAccountSamplePullCorrelationRule implements PullCorrelationRule { +@InboundCorrelationRuleConfClass(LinkedAccountSampleInboundCorrelationRuleConf.class) +public class LinkedAccountSampleInboundCorrelationRule implements InboundCorrelationRule { public static final String VIVALDI_KEY = "b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee"; @@ -43,7 +43,7 @@ public class LinkedAccountSamplePullCorrelationRule implements PullCorrelationRu private UserDAO userDAO; @Override - public SearchCond getSearchCond(final SyncDelta syncDelta, final Provision provision) { + public SearchCond getSearchCond(final LiveSyncDelta syncDelta, final Provision provision) { AttrCond cond = new AttrCond(); Attribute email = syncDelta.getObject().getAttributeByName("email"); @@ -60,7 +60,7 @@ public SearchCond getSearchCond(final SyncDelta syncDelta, final Provision provi @Transactional(readOnly = true) @Override - public PullMatch matching(final Any any, final SyncDelta syncDelta, final Provision provision) { + public InboundMatch matching(final Any any, final LiveSyncDelta syncDelta, final Provision provision) { // if match with internal user vivaldi was found but firstName is different, update linked account // instead of updating user Attribute firstName = syncDelta.getObject().getAttributeByName("firstName"); @@ -68,16 +68,16 @@ public PullMatch matching(final Any any, final SyncDelta syncDelta, final Pro && firstName != null && !CollectionUtils.isEmpty(firstName.getValue()) && !"Antonio".equals(firstName.getValue().get(0).toString())) { - return new PullMatch(MatchType.LINKED_ACCOUNT, any); + return new InboundMatch(MatchType.LINKED_ACCOUNT, any); } - return PullCorrelationRule.super.matching(any, syncDelta, provision); + return InboundCorrelationRule.super.matching(any, syncDelta, provision); } @Transactional(readOnly = true) @Override - public Optional unmatching(final SyncDelta syncDelta, final Provision provision) { + public Optional unmatching(final LiveSyncDelta syncDelta, final Provision provision) { // if no match with internal user was found, link account to vivaldi instead of creating new user - return Optional.of(new PullMatch(MatchType.LINKED_ACCOUNT, userDAO.findById(VIVALDI_KEY).orElseThrow())); + return Optional.of(new InboundMatch(MatchType.LINKED_ACCOUNT, userDAO.findById(VIVALDI_KEY).orElseThrow())); } } diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/LinkedAccountSamplePullCorrelationRuleConf.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/LinkedAccountSampleInboundCorrelationRuleConf.java similarity index 82% rename from fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/LinkedAccountSamplePullCorrelationRuleConf.java rename to fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/LinkedAccountSampleInboundCorrelationRuleConf.java index 4981c88bec6..c13a5a2a00c 100644 --- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/LinkedAccountSamplePullCorrelationRuleConf.java +++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/LinkedAccountSampleInboundCorrelationRuleConf.java @@ -19,10 +19,10 @@ package org.apache.syncope.fit.core.reference; import org.apache.syncope.common.lib.policy.AbstractCorrelationRuleConf; -import org.apache.syncope.common.lib.policy.PullCorrelationRuleConf; +import org.apache.syncope.common.lib.policy.InboundCorrelationRuleConf; -public class LinkedAccountSamplePullCorrelationRuleConf - extends AbstractCorrelationRuleConf implements PullCorrelationRuleConf { +public class LinkedAccountSampleInboundCorrelationRuleConf + extends AbstractCorrelationRuleConf implements InboundCorrelationRuleConf { private static final long serialVersionUID = -958386962492907926L; diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPullActions.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestInboundActions.java similarity index 86% rename from fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPullActions.java rename to fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestInboundActions.java index 2537287f212..a1b62dd8cd0 100644 --- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPullActions.java +++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestInboundActions.java @@ -26,20 +26,17 @@ import org.apache.syncope.common.lib.to.EntityTO; import org.apache.syncope.common.lib.types.PatchOperation; import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException; +import org.apache.syncope.core.provisioning.api.pushpull.InboundActions; import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile; -import org.apache.syncope.core.provisioning.api.pushpull.PullActions; -import org.identityconnectors.framework.common.objects.SyncDelta; +import org.identityconnectors.framework.common.objects.LiveSyncDelta; -/** - * Test pull action. - */ -public class TestPullActions implements PullActions { +public class TestInboundActions implements InboundActions { private int counter; @Override public void beforeProvision( - final ProvisioningProfile profile, final SyncDelta delta, final AnyCR anyCR) { + final ProvisioningProfile profile, final LiveSyncDelta delta, final AnyCR anyCR) { Attr attrTO = anyCR.getPlainAttrs().stream(). filter(attr -> "fullname".equals(attr.getSchema())).findFirst(). @@ -54,7 +51,7 @@ public void beforeProvision( @Override public void beforeAssign( - final ProvisioningProfile profile, final SyncDelta delta, final AnyCR anyCR) { + final ProvisioningProfile profile, final LiveSyncDelta delta, final AnyCR anyCR) { if (anyCR instanceof UserCR && "test2".equals(UserCR.class.cast(anyCR).getUsername())) { throw new IgnoreProvisionException(); @@ -64,7 +61,7 @@ public void beforeAssign( @Override public void beforeUpdate( final ProvisioningProfile profile, - final SyncDelta delta, + final LiveSyncDelta delta, final EntityTO entityTO, final AnyUR anyUR) { diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestLiveSyncDeltaMapper.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestLiveSyncDeltaMapper.java new file mode 100644 index 00000000000..11cb7204a8d --- /dev/null +++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestLiveSyncDeltaMapper.java @@ -0,0 +1,105 @@ +/* + * 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.fit.core.reference; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.json.JsonMapper; +import java.io.IOException; +import java.util.Optional; +import org.apache.syncope.common.lib.to.OrgUnit; +import org.apache.syncope.common.lib.to.Provision; +import org.apache.syncope.core.provisioning.api.LiveSyncDeltaMapper; +import org.identityconnectors.framework.common.objects.AttributeBuilder; +import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder; +import org.identityconnectors.framework.common.objects.LiveSyncDelta; +import org.identityconnectors.framework.common.objects.ObjectClass; +import org.identityconnectors.framework.common.objects.SyncDelta; +import org.identityconnectors.framework.common.objects.SyncDeltaBuilder; +import org.identityconnectors.framework.common.objects.SyncDeltaType; +import org.identityconnectors.framework.common.objects.SyncToken; +import org.identityconnectors.framework.common.objects.Uid; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.CollectionUtils; + +public class TestLiveSyncDeltaMapper implements LiveSyncDeltaMapper { + + private static final Logger LOG = LoggerFactory.getLogger(TestLiveSyncDeltaMapper.class); + + private static final JsonMapper JSON_MAPPER = JsonMapper.builder().findAndAddModules().build(); + + @Override + public SyncDelta map(final LiveSyncDelta liveSyncDelta, final OrgUnit orgUnit) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public SyncDelta map(final LiveSyncDelta liveSyncDelta, final Provision provision) { + if (!provision.getObjectClass().equals(liveSyncDelta.getObjectClass().getObjectClassValue())) { + throw new IllegalArgumentException("Expected " + provision.getObjectClass() + + ", got " + liveSyncDelta.getObjectClass().getObjectClassValue()); + } + + long timestamp = Optional.ofNullable(liveSyncDelta.getObject().getAttributeByName("record.timestamp")). + filter(attr -> !CollectionUtils.isEmpty(attr.getValue())). + map(attr -> (Long) attr.getValue().get(0)). + orElseThrow(() -> new IllegalArgumentException("No record.timestamp attribute found")); + String value = Optional.ofNullable(liveSyncDelta.getObject().getAttributeByName("record.value")). + filter(attr -> !CollectionUtils.isEmpty(attr.getValue())). + map(attr -> attr.getValue().get(0).toString()). + orElseThrow(() -> new IllegalArgumentException("No record.value attribute found")); + LOG.debug("Received: timestamp {} / value {}", timestamp, value); + + JsonNode tree; + try { + tree = JSON_MAPPER.readTree(value); + } catch (IOException e) { + throw new IllegalStateException("Could to parse the received value as JSON", e); + } + + SyncDeltaBuilder builder = new SyncDeltaBuilder(). + setToken(new SyncToken(timestamp)). + setDeltaType(SyncDeltaType.valueOf(tree.get("type").asText())); + if (ObjectClass.ACCOUNT.equals(liveSyncDelta.getObjectClass())) { + Uid uid = new Uid(tree.get("username").asText()); + builder.setObject(new ConnectorObjectBuilder(). + setObjectClass(liveSyncDelta.getObjectClass()). + setUid(uid). + setName(uid.getUidValue()). + addAttribute(AttributeBuilder.build("email", tree.get("email").asText())). + addAttribute(AttributeBuilder.build("givenName", tree.get("givenName").asText())). + addAttribute(AttributeBuilder.build("lastName", tree.get("lastName").asText())). + addAttribute(AttributeBuilder.build( + "fullname", + tree.get("givenName").asText() + " " + tree.get("lastName").asText())). + build()); + } else if (ObjectClass.GROUP.equals(liveSyncDelta.getObjectClass())) { + Uid uid = new Uid(tree.get("name").asText()); + builder.setObject(new ConnectorObjectBuilder(). + setObjectClass(liveSyncDelta.getObjectClass()). + setUid(uid). + setName(uid.getUidValue()). + build()); + } else { + throw new IllegalArgumentException("Unsupported ObjectClass: " + liveSyncDelta.getObjectClass()); + } + + return builder.build(); + } +} diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestSampleJobDelegate.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestSampleJobDelegate.java index 6f47a502236..fe6dadf6719 100644 --- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestSampleJobDelegate.java +++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestSampleJobDelegate.java @@ -31,9 +31,7 @@ public class TestSampleJobDelegate extends AbstractSchedTaskJobDelegate { @Override - protected String doExecute(final boolean dryRun, final String executor, final JobExecutionContext context) - throws JobExecutionException { - + protected String doExecute(final JobExecutionContext context) throws JobExecutionException { for (int i = 0; i < 2; i++) { LOG.debug("TestSampleJob#doExecute round {} time {}", i, OffsetDateTime.now()); try { @@ -43,11 +41,11 @@ protected String doExecute(final boolean dryRun, final String executor, final Jo } } - LOG.info("TestSampleJob {} running [SchedTask {}]", (dryRun + LOG.info("TestSampleJob {} running [SchedTask {}]", (context.isDryRun() ? "dry " : ""), task.getKey()); - return (dryRun + return (context.isDryRun() ? "DRY " : "") + "RUNNING"; } diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java index 6ceef5df772..a8d416be612 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java @@ -278,6 +278,8 @@ public void initialize(final ConfigurableApplicationContext ctx) { protected static final String RESOURCE_NAME_REST = "rest-target-resource"; + protected static final String RESOURCE_NAME_KAFKA = "resource-kafka"; + protected static final String RESOURCE_LDAP_ADMIN_DN = "uid=admin,ou=system"; protected static final String RESOURCE_LDAP_ADMIN_PWD = "secret"; diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/PoliciesITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/PoliciesITCase.java index 2ae10c69511..f13de47669e 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/PoliciesITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/PoliciesITCase.java @@ -96,7 +96,7 @@ private static void createAccountPolicy(final String name) { } private static void createPasswordPolicy(final String name) { - TESTER.clickLink("body:content:tabbedPanel:tabs-container:tabs:4:link"); + TESTER.clickLink("body:content:tabbedPanel:tabs-container:tabs:5:link"); TESTER.clickLink("body:content:tabbedPanel:panel:container:content:add"); TESTER.assertComponent("body:content:tabbedPanel:panel:outerObjectsRepeater:0:outer", Modal.class); @@ -145,8 +145,8 @@ private static void createPasswordPolicy(final String name) { + "searchContainer:resultTable:tablePanel:groupForm:checkgroup:dataTable", name)); } - private static void createPullPolicy(final String name) { - TESTER.clickLink("body:content:tabbedPanel:tabs-container:tabs:6:link"); + private static void createInboundPolicy(final String name) { + TESTER.clickLink("body:content:tabbedPanel:tabs-container:tabs:4:link"); TESTER.clickLink("body:content:tabbedPanel:panel:container:content:add"); TESTER.assertComponent("body:content:tabbedPanel:panel:outerObjectsRepeater:0:outer", Modal.class); @@ -216,7 +216,7 @@ private static void deleteAccountPolicy(final String name) { } private static void deletePasswordPolicy(final String name) { - TESTER.clickLink("body:content:tabbedPanel:tabs-container:tabs:4:link"); + TESTER.clickLink("body:content:tabbedPanel:tabs-container:tabs:5:link"); Component component = findComponentByProp("name", "body:content:tabbedPanel:panel:container:content:" + "searchContainer:resultTable:tablePanel:groupForm:checkgroup:dataTable", name); @@ -240,8 +240,8 @@ private static void deletePasswordPolicy(final String name) { + "searchContainer:resultTable:tablePanel:groupForm:checkgroup:dataTable", name)); } - private static void deletePullPolicy(final String name) { - TESTER.clickLink("body:content:tabbedPanel:tabs-container:tabs:6:link"); + private static void deleteInboundPolicy(final String name) { + TESTER.clickLink("body:content:tabbedPanel:tabs-container:tabs:4:link"); Component component = findComponentByProp("name", "body:content:tabbedPanel:panel:container:content:" + "searchContainer:resultTable:tablePanel:groupForm:checkgroup:dataTable", name); @@ -375,16 +375,16 @@ public void cloneDeletePasswordPolicy() { } @Test - public void createDeletePullPolicy() { + public void createDeleteInboundPolicy() { String name = "My Test Pull Policy"; - createPullPolicy(name); - deletePullPolicy(name); + createInboundPolicy(name); + deleteInboundPolicy(name); } @Test - public void cloneDeletePullPolicy() { + public void cloneDeleteInboundPolicy() { String name = "My Test Pull Policy to be cloned"; - createPullPolicy(name); + createInboundPolicy(name); Component component = findComponentByProp("name", "body:content:tabbedPanel:panel:container:content:" @@ -417,12 +417,12 @@ public void cloneDeletePullPolicy() { assertNotNull(findComponentByProp("name", "body:content:tabbedPanel:panel:container:content:" + "searchContainer:resultTable:tablePanel:groupForm:checkgroup:dataTable", name)); - deletePullPolicy(name); + deleteInboundPolicy(name); assertNotNull(findComponentByProp("name", "body:content:tabbedPanel:panel:container:content:" + "searchContainer:resultTable:tablePanel:groupForm:checkgroup:dataTable", name + '2')); - deletePullPolicy(name + '2'); + deleteInboundPolicy(name + '2'); } @Test @@ -628,9 +628,9 @@ public void createComposeDeletePasswordPolicy() { } @Test - public void createUpdateDeletePullPolicy() { + public void createUpdateDeleteInboundPolicy() { String name = "Pull Policy To Be Updated"; - createPullPolicy(name); + createInboundPolicy(name); Component component = findComponentByProp("name", "body:content:tabbedPanel:panel:container:content:" @@ -679,13 +679,13 @@ public void createUpdateDeletePullPolicy() { "body:content:tabbedPanel:panel:outerObjectsRepeater:0:outer:dialog:footer:buttons:0:button", Constants.ON_CLICK); - deletePullPolicy(name + '2'); + deleteInboundPolicy(name + '2'); } @Test - public void createComposeDeletePullPolicy() { + public void createComposeDeleteInboundPolicy() { String name = "Pull Policy To Be Composed"; - createPullPolicy(name); + createInboundPolicy(name); Component component = findComponentByProp("name", "body:content:tabbedPanel:panel:container:content:" @@ -728,7 +728,7 @@ public void createComposeDeletePullPolicy() { closeCallBack(modal); - deletePullPolicy(name); + deleteInboundPolicy(name); } @Test diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/TopologyITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/TopologyITCase.java index 0d77c28b885..e1877f431a2 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/console/TopologyITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/console/TopologyITCase.java @@ -20,6 +20,7 @@ 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 de.agilecoders.wicket.core.markup.html.bootstrap.dialog.Modal; import java.util.UUID; @@ -156,16 +157,23 @@ public void editProvisioning() { @Test public void createNewResurceAndProvisionRules() { - String res = UUID.randomUUID().toString(); + // ConnInstance100 + Component connector = findComponentByProp( + "key", "body:conns:0:conns", "88a7a819-dab5-46b4-9b90-0b9769eabdb8"); + assertNotNull(connector); + + int bodyIdx = connector.getPath().indexOf("body"); + assertTrue(bodyIdx != -1); TESTER.executeAjaxEvent( - "body:conns:0:conns:1:conn", Constants.ON_CLICK); + connector.getPath().substring(bodyIdx) + ":conn", Constants.ON_CLICK); TESTER.executeAjaxEvent( "body:toggle:container:content:togglePanelContainer:container:actions:create", Constants.ON_CLICK); FormTester formTester = TESTER.newFormTester( "body:toggle:outerObjectsRepeater:0:outer:form:content:form"); + String res = UUID.randomUUID().toString(); formTester.setValue("view:container:key:textField", res); formTester.submit("buttons:next"); diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java index 6ce27ee5d9b..2bc4757c0fb 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ConnectorITCase.java @@ -29,11 +29,13 @@ import jakarta.ws.rs.core.Response; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; import java.util.Collection; import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Optional; import java.util.Properties; import java.util.Set; import java.util.UUID; @@ -631,7 +633,7 @@ public void issueSYNCOPE112() { connectorTO.setDisplayName("WSSoap"); // set the connector configuration using PropertyTO - Set conf = new HashSet<>(); + List conf = new ArrayList<>(); ConnConfPropSchema userSchema = new ConnConfPropSchema(); userSchema.setName("endpoint"); @@ -683,12 +685,12 @@ public void issueSYNCOPE112() { resourceTO.setKey(resourceName); resourceTO.setConnector(connectorTO.getKey()); - conf = new HashSet<>(); + conf = new ArrayList<>(); endpoint.getValues().clear(); endpoint.getValues().add(BUILD_TOOLS_ADDRESS + "/soap/provisioning"); conf.add(endpoint); - resourceTO.getConfOverride().addAll(conf); + resourceTO.setConfOverride(Optional.of(conf)); Provision provisionTO = new Provision(); provisionTO.setAnyType(AnyTypeKind.USER.name()); diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java index 1dfe9e2eeed..f2ff384bee1 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/GroupITCase.java @@ -36,6 +36,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; @@ -842,7 +843,6 @@ public void capabilitiesOverride() { // resource with no capability override ResourceTO ldap = RESOURCE_SERVICE.read(RESOURCE_NAME_LDAP); assertNotNull(ldap); - assertFalse(ldap.isOverrideCapabilities()); assertTrue(ldap.getCapabilitiesOverride().isEmpty()); // connector with all required for create and update @@ -865,63 +865,33 @@ public void capabilitiesOverride() { GroupTO group = result.getEntity(); // 2. update succeeds - GroupUR groupUR = new GroupUR(); - groupUR.setKey(group.getKey()); - groupUR.getPlainAttrs().add(new AttrPatch.Builder(attr("title", "second")). - operation(PatchOperation.ADD_REPLACE).build()); - - result = updateGroup(groupUR); - assertNotNull(result); - assertEquals(1, result.getPropagationStatuses().size()); - assertEquals(RESOURCE_NAME_LDAP, result.getPropagationStatuses().get(0).getResource()); - assertEquals(ExecStatus.SUCCESS, result.getPropagationStatuses().get(0).getStatus()); - group = result.getEntity(); - - // 3. set capability override with only search allowed, but not enable - ldap.getCapabilitiesOverride().add(ConnectorCapability.SEARCH); - RESOURCE_SERVICE.update(ldap); - ldap = RESOURCE_SERVICE.read(RESOURCE_NAME_LDAP); - assertNotNull(ldap); - assertFalse(ldap.isOverrideCapabilities()); - assertEquals(1, ldap.getCapabilitiesOverride().size()); - assertTrue(ldap.getCapabilitiesOverride().contains(ConnectorCapability.SEARCH)); - - // 4. update succeeds again - groupUR = new GroupUR(); - groupUR.setKey(group.getKey()); - groupUR.getPlainAttrs().add(new AttrPatch.Builder(attr("title", "third")). - operation(PatchOperation.ADD_REPLACE).build()); - - result = updateGroup(groupUR); + result = updateGroup(new GroupUR.Builder(group.getKey()). + plainAttr(new AttrPatch.Builder(attr("title", "second")).build()). + build()); assertNotNull(result); assertEquals(1, result.getPropagationStatuses().size()); assertEquals(RESOURCE_NAME_LDAP, result.getPropagationStatuses().get(0).getResource()); assertEquals(ExecStatus.SUCCESS, result.getPropagationStatuses().get(0).getStatus()); group = result.getEntity(); - // 5. enable capability override - ldap.setOverrideCapabilities(true); + // 3. enable capability override with only search allowed + ldap.setCapabilitiesOverride(Optional.of(Set.of(ConnectorCapability.SEARCH))); RESOURCE_SERVICE.update(ldap); ldap = RESOURCE_SERVICE.read(RESOURCE_NAME_LDAP); assertNotNull(ldap); - assertTrue(ldap.isOverrideCapabilities()); - assertEquals(1, ldap.getCapabilitiesOverride().size()); - assertTrue(ldap.getCapabilitiesOverride().contains(ConnectorCapability.SEARCH)); - - // 6. update now fails - groupUR = new GroupUR(); - groupUR.setKey(group.getKey()); - groupUR.getPlainAttrs().add(new AttrPatch.Builder(attr("title", "fourth")). - operation(PatchOperation.ADD_REPLACE).build()); + assertEquals(1, ldap.getCapabilitiesOverride().orElseThrow().size()); + assertTrue(ldap.getCapabilitiesOverride().orElseThrow().contains(ConnectorCapability.SEARCH)); - result = updateGroup(groupUR); + // 4. update now fails + result = updateGroup(new GroupUR.Builder(group.getKey()). + plainAttr(new AttrPatch.Builder(attr("title", "fourth")).build()). + build()); assertNotNull(result); assertEquals(1, result.getPropagationStatuses().size()); assertEquals(RESOURCE_NAME_LDAP, result.getPropagationStatuses().get(0).getResource()); assertEquals(ExecStatus.NOT_ATTEMPTED, result.getPropagationStatuses().get(0).getStatus()); } finally { - ldap.getCapabilitiesOverride().clear(); - ldap.setOverrideCapabilities(false); + ldap.setCapabilitiesOverride(Optional.empty()); RESOURCE_SERVICE.update(ldap); } } diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ImplementationITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ImplementationITCase.java index f2b354ec471..d626830001f 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ImplementationITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ImplementationITCase.java @@ -33,7 +33,7 @@ import org.apache.syncope.common.lib.types.TaskType; import org.apache.syncope.common.rest.api.RESTHeaders; import org.apache.syncope.fit.AbstractITCase; -import org.apache.syncope.fit.core.reference.TestPullActions; +import org.apache.syncope.fit.core.reference.TestInboundActions; import org.junit.jupiter.api.Test; public class ImplementationITCase extends AbstractITCase { @@ -44,7 +44,7 @@ public void create() { implementationTO.setKey(UUID.randomUUID().toString()); implementationTO.setEngine(ImplementationEngine.JAVA); implementationTO.setType(IdMImplementationType.PUSH_ACTIONS); - implementationTO.setBody(TestPullActions.class.getName()); + implementationTO.setBody(TestInboundActions.class.getName()); // fail because type is wrong try { @@ -53,7 +53,7 @@ public void create() { } catch (SyncopeClientException e) { assertEquals(ClientExceptionType.InvalidImplementation, e.getType()); } - implementationTO.setType(IdMImplementationType.PULL_ACTIONS); + implementationTO.setType(IdMImplementationType.INBOUND_ACTIONS); Response response = IMPLEMENTATION_SERVICE.create(implementationTO); if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) { @@ -74,8 +74,8 @@ public void delete() { ImplementationTO implementationTO = new ImplementationTO(); implementationTO.setKey(UUID.randomUUID().toString()); implementationTO.setEngine(ImplementationEngine.JAVA); - implementationTO.setType(IdMImplementationType.PULL_ACTIONS); - implementationTO.setBody(TestPullActions.class.getName()); + implementationTO.setType(IdMImplementationType.INBOUND_ACTIONS); + implementationTO.setBody(TestInboundActions.class.getName()); IMPLEMENTATION_SERVICE.create(implementationTO); diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LinkedAccountITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LinkedAccountITCase.java index 61b38a3ba10..86b6a083e38 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LinkedAccountITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LinkedAccountITCase.java @@ -40,7 +40,7 @@ import org.apache.cxf.jaxrs.client.WebClient; import org.apache.syncope.common.lib.SyncopeClientException; import org.apache.syncope.common.lib.SyncopeConstants; -import org.apache.syncope.common.lib.policy.PullPolicyTO; +import org.apache.syncope.common.lib.policy.InboundPolicyTO; import org.apache.syncope.common.lib.request.LinkedAccountUR; import org.apache.syncope.common.lib.request.UserCR; import org.apache.syncope.common.lib.request.UserUR; @@ -72,8 +72,8 @@ import org.apache.syncope.common.rest.api.service.TaskService; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; import org.apache.syncope.fit.AbstractITCase; -import org.apache.syncope.fit.core.reference.LinkedAccountSamplePullCorrelationRule; -import org.apache.syncope.fit.core.reference.LinkedAccountSamplePullCorrelationRuleConf; +import org.apache.syncope.fit.core.reference.LinkedAccountSampleInboundCorrelationRule; +import org.apache.syncope.fit.core.reference.LinkedAccountSampleInboundCorrelationRuleConf; import org.junit.jupiter.api.Test; public class LinkedAccountITCase extends AbstractITCase { @@ -410,18 +410,19 @@ public void pull() { // Add a custom policy with correlation rule // ----------------------------- ResourceTO restResource = RESOURCE_SERVICE.read(RESOURCE_NAME_REST); - if (restResource.getPullPolicy() == null) { + if (restResource.getInboundPolicy() == null) { ImplementationTO rule = null; try { rule = IMPLEMENTATION_SERVICE.read( - IdMImplementationType.PULL_CORRELATION_RULE, "LinkedAccountSamplePullCorrelationRule"); + IdMImplementationType.INBOUND_CORRELATION_RULE, + "LinkedAccountSampleInboundCorrelationrrelationRule"); } catch (SyncopeClientException e) { if (e.getType().getResponseStatus() == Response.Status.NOT_FOUND) { rule = new ImplementationTO(); - rule.setKey("LinkedAccountSamplePullCorrelationRule"); + rule.setKey("LinkedAccountSampleInboundCorrelationrrelationRule"); rule.setEngine(ImplementationEngine.JAVA); - rule.setType(IdMImplementationType.PULL_CORRELATION_RULE); - rule.setBody(POJOHelper.serialize(new LinkedAccountSamplePullCorrelationRuleConf())); + rule.setType(IdMImplementationType.INBOUND_CORRELATION_RULE); + rule.setBody(POJOHelper.serialize(new LinkedAccountSampleInboundCorrelationRuleConf())); Response response = IMPLEMENTATION_SERVICE.create(rule); rule = IMPLEMENTATION_SERVICE.read( rule.getType(), response.getHeaderString(RESTHeaders.RESOURCE_KEY)); @@ -430,14 +431,14 @@ public void pull() { } assertNotNull(rule); - PullPolicyTO policy = new PullPolicyTO(); - policy.setName("Linked Account sample Pull policy"); + InboundPolicyTO policy = new InboundPolicyTO(); + policy.setName("Linked Account sample inbound policy"); policy.getCorrelationRules().put(AnyTypeKind.USER.name(), rule.getKey()); - Response response = POLICY_SERVICE.create(PolicyType.PULL, policy); - policy = POLICY_SERVICE.read(PolicyType.PULL, response.getHeaderString(RESTHeaders.RESOURCE_KEY)); + Response response = POLICY_SERVICE.create(PolicyType.INBOUND, policy); + policy = POLICY_SERVICE.read(PolicyType.INBOUND, response.getHeaderString(RESTHeaders.RESOURCE_KEY)); assertNotNull(policy.getKey()); - restResource.setPullPolicy(policy.getKey()); + restResource.setInboundPolicy(policy.getKey()); RESOURCE_SERVICE.update(restResource); } @@ -595,7 +596,7 @@ public void pull() { } finally { // clean up UserUR patch = new UserUR(); - patch.setKey(LinkedAccountSamplePullCorrelationRule.VIVALDI_KEY); + patch.setKey(LinkedAccountSampleInboundCorrelationRule.VIVALDI_KEY); patch.getLinkedAccounts().add(new LinkedAccountUR.Builder(). operation(PatchOperation.DELETE). linkedAccountTO(new LinkedAccountTO.Builder(RESOURCE_NAME_REST, user2Key).build()). diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LiveSyncITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LiveSyncITCase.java new file mode 100644 index 00000000000..af21cceaa6a --- /dev/null +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LiveSyncITCase.java @@ -0,0 +1,298 @@ +/* + * 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.fit.core; + +import static org.awaitility.Awaitility.await; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import com.fasterxml.jackson.databind.JsonNode; +import jakarta.ws.rs.core.Response; +import java.io.IOException; +import java.time.Duration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import org.apache.kafka.clients.consumer.ConsumerConfig; +import org.apache.kafka.clients.consumer.KafkaConsumer; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.common.serialization.Serdes; +import org.apache.kafka.common.serialization.StringSerializer; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.SyncopeConstants; +import org.apache.syncope.common.lib.request.UserCR; +import org.apache.syncope.common.lib.request.UserUR; +import org.apache.syncope.common.lib.to.ImplementationTO; +import org.apache.syncope.common.lib.to.LiveSyncTaskTO; +import org.apache.syncope.common.lib.to.ProvisioningResult; +import org.apache.syncope.common.lib.to.UserTO; +import org.apache.syncope.common.lib.types.ExecStatus; +import org.apache.syncope.common.lib.types.IdMImplementationType; +import org.apache.syncope.common.lib.types.ImplementationEngine; +import org.apache.syncope.common.lib.types.JobAction; +import org.apache.syncope.common.lib.types.TaskType; +import org.apache.syncope.common.rest.api.RESTHeaders; +import org.apache.syncope.common.rest.api.beans.ExecSpecs; +import org.apache.syncope.common.rest.api.service.TaskService; +import org.apache.syncope.core.provisioning.java.pushpull.KafkaInboundActions; +import org.apache.syncope.fit.AbstractITCase; +import org.apache.syncope.fit.core.reference.TestLiveSyncDeltaMapper; +import org.identityconnectors.framework.common.objects.SyncDeltaType; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class LiveSyncITCase extends AbstractITCase { + + private static final String ACCOUNT_TOPIC = "account-provisioning"; + + private static final String GROUP_TOPIC = "group-provisioning"; + + @BeforeAll + public static void testLiveSyncImplementationSetup() { + ImplementationTO liveSDM = null; + try { + liveSDM = IMPLEMENTATION_SERVICE.read( + IdMImplementationType.LIVE_SYNC_DELTA_MAPPER, TestLiveSyncDeltaMapper.class.getSimpleName()); + } catch (SyncopeClientException e) { + if (e.getType().getResponseStatus() == Response.Status.NOT_FOUND) { + liveSDM = new ImplementationTO(); + liveSDM.setKey(TestLiveSyncDeltaMapper.class.getSimpleName()); + liveSDM.setEngine(ImplementationEngine.JAVA); + liveSDM.setType(IdMImplementationType.LIVE_SYNC_DELTA_MAPPER); + liveSDM.setBody(TestLiveSyncDeltaMapper.class.getName()); + Response response = IMPLEMENTATION_SERVICE.create(liveSDM); + liveSDM = IMPLEMENTATION_SERVICE.read( + liveSDM.getType(), response.getHeaderString(RESTHeaders.RESOURCE_KEY)); + assertNotNull(liveSDM); + } + } + assertNotNull(liveSDM); + + ImplementationTO kafkaInboundActions = null; + try { + kafkaInboundActions = IMPLEMENTATION_SERVICE.read( + IdMImplementationType.INBOUND_ACTIONS, KafkaInboundActions.class.getSimpleName()); + } catch (SyncopeClientException e) { + if (e.getType().getResponseStatus() == Response.Status.NOT_FOUND) { + kafkaInboundActions = new ImplementationTO(); + kafkaInboundActions.setKey(KafkaInboundActions.class.getSimpleName()); + kafkaInboundActions.setEngine(ImplementationEngine.JAVA); + kafkaInboundActions.setType(IdMImplementationType.INBOUND_ACTIONS); + kafkaInboundActions.setBody(KafkaInboundActions.class.getName()); + Response response = IMPLEMENTATION_SERVICE.create(kafkaInboundActions); + kafkaInboundActions = IMPLEMENTATION_SERVICE.read( + kafkaInboundActions.getType(), response.getHeaderString(RESTHeaders.RESOURCE_KEY)); + assertNotNull(kafkaInboundActions); + } + } + assertNotNull(kafkaInboundActions); + } + + private static KafkaProducer createProducer() { + Map props = new HashMap<>(); + props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:19092"); + props.put(ProducerConfig.CLIENT_ID_CONFIG, LiveSyncITCase.class.getSimpleName()); + props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); + props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName()); + + return new KafkaProducer<>(props); + } + + private static KafkaConsumer createConsumer() { + Map props = new HashMap<>(); + props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:19092"); + props.put(ConsumerConfig.CLIENT_ID_CONFIG, LiveSyncITCase.class.getSimpleName()); + props.put(ConsumerConfig.GROUP_ID_CONFIG, LiveSyncITCase.class.getSimpleName()); + props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest"); + + KafkaConsumer consumer = new KafkaConsumer<>( + props, + Serdes.String().deserializer(), + Serdes.String().deserializer()); + consumer.subscribe(List.of(ACCOUNT_TOPIC, GROUP_TOPIC)); + return consumer; + } + + private static boolean found(final SyncDeltaType syncDeltaType, final String username) { + AtomicBoolean found = new AtomicBoolean(false); + try (KafkaConsumer consumer = createConsumer()) { + consumer.poll(Duration.ofSeconds(10)).forEach(record -> { + if (ACCOUNT_TOPIC.equals(record.topic())) { + SyncDeltaType sdt = null; + String uid = null; + try { + JsonNode syncDelta = JSON_MAPPER.readTree(record.value()); + if (syncDelta.has("deltaType")) { + sdt = SyncDeltaType.valueOf(syncDelta.get("deltaType").asText()); + } + if (syncDelta.has("uid") && syncDelta.get("uid").has("value")) { + uid = syncDelta.get("uid").get("value").iterator().next().asText(); + } + } catch (IOException e) { + fail(e.getMessage(), e); + } + + found.set(syncDeltaType == sdt && username.equals(uid)); + } + }); + } + return found.get(); + } + + @Test + public void crud() { + UserCR userCR = UserITCase.getUniqueSample("kafka@syncope.apache.org"); + userCR.getResources().add(RESOURCE_NAME_KAFKA); + ProvisioningResult created = createUser(userCR); + assertEquals(RESOURCE_NAME_KAFKA, created.getPropagationStatuses().get(0).getResource()); + assertEquals(ExecStatus.SUCCESS, created.getPropagationStatuses().get(0).getStatus()); + + assertTrue(found(SyncDeltaType.CREATE, created.getEntity().getUsername())); + + UserUR req = new UserUR(); + req.setKey(created.getEntity().getKey()); + req.getPlainAttrs().add(attrAddReplacePatch("firstname", "Updated")); + ProvisioningResult updated = updateUser(req); + assertEquals(RESOURCE_NAME_KAFKA, updated.getPropagationStatuses().get(0).getResource()); + assertEquals(ExecStatus.SUCCESS, updated.getPropagationStatuses().get(0).getStatus()); + + assertTrue(found(SyncDeltaType.UPDATE, updated.getEntity().getUsername())); + + deleteUser(created.getEntity().getKey()); + + assertTrue(found(SyncDeltaType.DELETE, updated.getEntity().getUsername())); + } + + @Test + public void liveSync() { + // 1. create and execute the live sync task + LiveSyncTaskTO task = new LiveSyncTaskTO(); + task.setName("Test LiveSync"); + task.setDestinationRealm(SyncopeConstants.ROOT_REALM); + task.setResource(RESOURCE_NAME_KAFKA); + task.setLiveSyncDeltaMapper(TestLiveSyncDeltaMapper.class.getSimpleName()); + task.getActions().add(KafkaInboundActions.class.getSimpleName()); + task.setPerformCreate(true); + task.setPerformUpdate(true); + task.setPerformDelete(true); + + Response response = TASK_SERVICE.create(TaskType.LIVE_SYNC, task); + LiveSyncTaskTO actual = getObject(response.getLocation(), TaskService.class, LiveSyncTaskTO.class); + assertNotNull(actual); + + task = TASK_SERVICE.read(TaskType.LIVE_SYNC, actual.getKey(), true); + assertNotNull(task); + assertEquals(actual.getKey(), task.getKey()); + assertNotNull(actual.getJobDelegate()); + assertEquals(actual.getLiveSyncDeltaMapper(), task.getLiveSyncDeltaMapper()); + + TASK_SERVICE.execute(new ExecSpecs.Builder().key(task.getKey()).build()); + + try { + // 2. send event to the queue + String email = "liveSync" + getUUIDString() + "@syncope.apache.org"; + try (KafkaProducer producer = createProducer()) { + producer.send(new ProducerRecord<>( + ACCOUNT_TOPIC, + UUID.randomUUID().toString(), + """ + { + "username": "%s", + "email": "%s", + "givenName": "LiveSync", + "lastName": "LiveSync", + "type": "CREATE_OR_UPDATE" + } + """. + formatted(email, email))); + } + + // 3. find the user created in Syncope + UserTO user = await().atMost(MAX_WAIT_SECONDS, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).until( + () -> { + try { + return USER_SERVICE.read(email); + } catch (SyncopeClientException e) { + return null; + } + }, Objects::nonNull); + assertEquals(email, user.getPlainAttr("email").orElseThrow().getValues().get(0)); + assertEquals(email, user.getPlainAttr("userId").orElseThrow().getValues().get(0)); + assertEquals(email, user.getPlainAttr("userId").orElseThrow().getValues().get(0)); + assertEquals("LiveSync", user.getPlainAttr("firstname").orElseThrow().getValues().get(0)); + assertEquals("LiveSync", user.getPlainAttr("surname").orElseThrow().getValues().get(0)); + + await().atMost(MAX_WAIT_SECONDS, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).until( + () -> TASK_SERVICE.read(TaskType.LIVE_SYNC, actual.getKey(), true).getExecutions(), + execs -> execs.size() == 1 + && execs.stream().allMatch(exec -> ExecStatus.SUCCESS.name().equals(exec.getStatus()))); + + // 4. stop live syncing + assertTrue(TASK_SERVICE.getJob(task.getKey()).isRunning()); + + TASK_SERVICE.actionJob(task.getKey(), JobAction.STOP); + + await().atMost(MAX_WAIT_SECONDS, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS). + until(() -> !TASK_SERVICE.getJob(actual.getKey()).isRunning()); + + // 5. send new event to the queue, but no further executions + String groupName = "liveSync" + getUUIDString(); + try (KafkaProducer producer = createProducer()) { + producer.send(new ProducerRecord<>( + GROUP_TOPIC, + UUID.randomUUID().toString(), + """ + { + "name": "%s", + "type": "CREATE_OR_UPDATE" + } + """. + formatted(groupName))); + } + + // 6. start again live syncing and find the new group in Syncope + TASK_SERVICE.actionJob(task.getKey(), JobAction.START); + + await().atMost(MAX_WAIT_SECONDS, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).until( + () -> { + try { + return GROUP_SERVICE.read(groupName); + } catch (SyncopeClientException e) { + return null; + } + }, Objects::nonNull); + + await().atMost(MAX_WAIT_SECONDS, TimeUnit.SECONDS).pollInterval(1, TimeUnit.SECONDS).until( + () -> TASK_SERVICE.read(TaskType.LIVE_SYNC, actual.getKey(), true).getExecutions(), + execs -> execs.size() == 2 + && execs.stream().allMatch(exec -> ExecStatus.SUCCESS.name().equals(exec.getStatus()))); + } finally { + // finally stop live syncing + TASK_SERVICE.actionJob(task.getKey(), JobAction.STOP); + } + } +} diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PolicyITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PolicyITCase.java index 5b95a69eb29..53b3f9dea58 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PolicyITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PolicyITCase.java @@ -43,9 +43,9 @@ import org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf; import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf; import org.apache.syncope.common.lib.policy.DefaultTicketExpirationPolicyConf; +import org.apache.syncope.common.lib.policy.InboundPolicyTO; import org.apache.syncope.common.lib.policy.PasswordPolicyTO; import org.apache.syncope.common.lib.policy.PropagationPolicyTO; -import org.apache.syncope.common.lib.policy.PullPolicyTO; import org.apache.syncope.common.lib.policy.PushPolicyTO; import org.apache.syncope.common.lib.policy.TicketExpirationPolicyTO; import org.apache.syncope.common.lib.to.ImplementationTO; @@ -59,22 +59,22 @@ import org.apache.syncope.common.rest.api.RESTHeaders; import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; import org.apache.syncope.fit.AbstractITCase; -import org.apache.syncope.fit.core.reference.DummyPullCorrelationRule; +import org.apache.syncope.fit.core.reference.DummyInboundCorrelationRule; import org.apache.syncope.fit.core.reference.DummyPushCorrelationRule; import org.junit.jupiter.api.Test; public class PolicyITCase extends AbstractITCase { - private PullPolicyTO buildPullPolicyTO() throws IOException { + private InboundPolicyTO buildInboundPolicyTO() throws IOException { ImplementationTO corrRule = null; try { - corrRule = IMPLEMENTATION_SERVICE.read(IdMImplementationType.PULL_CORRELATION_RULE, "TestPullRule"); + corrRule = IMPLEMENTATION_SERVICE.read(IdMImplementationType.INBOUND_CORRELATION_RULE, "TestPullRule"); } catch (SyncopeClientException e) { if (e.getType().getResponseStatus() == Response.Status.NOT_FOUND) { corrRule = new ImplementationTO(); corrRule.setKey("TestPullRule"); corrRule.setEngine(ImplementationEngine.GROOVY); - corrRule.setType(IdMImplementationType.PULL_CORRELATION_RULE); + corrRule.setType(IdMImplementationType.INBOUND_CORRELATION_RULE); corrRule.setBody(IOUtils.toString( getClass().getResourceAsStream("/TestPullRule.groovy"), StandardCharsets.UTF_8)); Response response = IMPLEMENTATION_SERVICE.create(corrRule); @@ -85,7 +85,7 @@ private PullPolicyTO buildPullPolicyTO() throws IOException { } assertNotNull(corrRule); - PullPolicyTO policy = new PullPolicyTO(); + InboundPolicyTO policy = new InboundPolicyTO(); policy.getCorrelationRules().put(AnyTypeKind.USER.name(), corrRule.getKey()); policy.setName("Pull policy"); @@ -125,7 +125,7 @@ public void listByType() { assertNotNull(propagationPolicies); assertFalse(propagationPolicies.isEmpty()); - List pullPolicies = POLICY_SERVICE.list(PolicyType.PULL); + List pullPolicies = POLICY_SERVICE.list(PolicyType.INBOUND); assertNotNull(pullPolicies); assertFalse(pullPolicies.isEmpty()); } @@ -159,8 +159,8 @@ public void getPropagationPolicy() { } @Test - public void getPullPolicy() { - PullPolicyTO policyTO = POLICY_SERVICE.read(PolicyType.PULL, "66691e96-285f-4464-bc19-e68384ea4c85"); + public void getInboundPolicy() { + InboundPolicyTO policyTO = POLICY_SERVICE.read(PolicyType.INBOUND, "66691e96-285f-4464-bc19-e68384ea4c85"); assertNotNull(policyTO); assertTrue(policyTO.getUsedByRealms().isEmpty()); @@ -201,9 +201,9 @@ public void create() throws IOException { assertEquals(3, propagationPolicyTO.getMaxAttempts()); assertEquals(BackOffStrategy.EXPONENTIAL.getDefaultBackOffParams(), propagationPolicyTO.getBackOffParams()); - PullPolicyTO pullPolicyTO = createPolicy(PolicyType.PULL, buildPullPolicyTO()); - assertNotNull(pullPolicyTO); - assertEquals("TestPullRule", pullPolicyTO.getCorrelationRules().get(AnyTypeKind.USER.name())); + InboundPolicyTO inboundPolicyTO = createPolicy(PolicyType.INBOUND, buildInboundPolicyTO()); + assertNotNull(inboundPolicyTO); + assertEquals("TestPullRule", inboundPolicyTO.getCorrelationRules().get(AnyTypeKind.USER.name())); PushPolicyTO pushPolicyTO = createPolicy(PolicyType.PUSH, buildPushPolicyTO()); assertNotNull(pushPolicyTO); @@ -340,15 +340,15 @@ public void updateTicketExpirationPolicy() { @Test public void delete() throws IOException { - PullPolicyTO policy = buildPullPolicyTO(); + InboundPolicyTO policy = buildInboundPolicyTO(); - PullPolicyTO policyTO = createPolicy(PolicyType.PULL, policy); + InboundPolicyTO policyTO = createPolicy(PolicyType.INBOUND, policy); assertNotNull(policyTO); - POLICY_SERVICE.delete(PolicyType.PULL, policyTO.getKey()); + POLICY_SERVICE.delete(PolicyType.INBOUND, policyTO.getKey()); try { - POLICY_SERVICE.read(PolicyType.PULL, policyTO.getKey()); + POLICY_SERVICE.read(PolicyType.INBOUND, policyTO.getKey()); fail("This should not happen"); } catch (SyncopeClientException e) { assertNotNull(e); @@ -407,11 +407,11 @@ public void delete() throws IOException { } @Test - public void getPullCorrelationRuleJavaClasses() { + public void getInboundCorrelationRuleJavaClasses() { Set classes = ANONYMOUS_CLIENT.platform(). - getJavaImplInfo(IdMImplementationType.PULL_CORRELATION_RULE).get().getClasses(); + getJavaImplInfo(IdMImplementationType.INBOUND_CORRELATION_RULE).get().getClasses(); assertEquals(1, classes.size()); - assertEquals(DummyPullCorrelationRule.class.getName(), classes.iterator().next()); + assertEquals(DummyInboundCorrelationRule.class.getName(), classes.iterator().next()); } @Test diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java index 16548d3e5b9..cd30b42112e 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/PropagationTaskITCase.java @@ -217,10 +217,10 @@ public void batch() throws IOException { } @Test - public void propagationJEXLTransformer() { + public void propagationJEXLTransformer() throws IOException { // 0. Set propagation JEXL MappingItemTransformer ResourceTO resource = RESOURCE_SERVICE.read(RESOURCE_NAME_DBSCRIPTED); - ResourceTO originalResource = SerializationUtils.clone(resource); + ResourceTO originalResource = RESOURCE_SERVICE.read(RESOURCE_NAME_DBSCRIPTED); Provision provision = resource.getProvision(PRINTER).orElseThrow(); assertNotNull(provision); 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 14edddf2550..807214cf91a 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 @@ -63,7 +63,7 @@ import org.apache.syncope.common.lib.Attr; import org.apache.syncope.common.lib.SyncopeClientException; import org.apache.syncope.common.lib.SyncopeConstants; -import org.apache.syncope.common.lib.policy.PullPolicyTO; +import org.apache.syncope.common.lib.policy.InboundPolicyTO; import org.apache.syncope.common.lib.request.AnyCR; import org.apache.syncope.common.lib.request.AnyObjectCR; import org.apache.syncope.common.lib.request.GroupCR; @@ -115,7 +115,7 @@ import org.apache.syncope.core.provisioning.java.pushpull.DBPasswordPullActions; import org.apache.syncope.core.provisioning.java.pushpull.LDAPPasswordPullActions; import org.apache.syncope.core.spring.security.Encryptor; -import org.apache.syncope.fit.core.reference.TestPullActions; +import org.apache.syncope.fit.core.reference.TestInboundActions; import org.identityconnectors.framework.common.objects.Name; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -131,14 +131,14 @@ public static void testPullActionsSetup() { ImplementationTO pullActions = null; try { pullActions = IMPLEMENTATION_SERVICE.read( - IdMImplementationType.PULL_ACTIONS, TestPullActions.class.getSimpleName()); + IdMImplementationType.INBOUND_ACTIONS, TestInboundActions.class.getSimpleName()); } catch (SyncopeClientException e) { if (e.getType().getResponseStatus() == Response.Status.NOT_FOUND) { pullActions = new ImplementationTO(); - pullActions.setKey(TestPullActions.class.getSimpleName()); + pullActions.setKey(TestInboundActions.class.getSimpleName()); pullActions.setEngine(ImplementationEngine.JAVA); - pullActions.setType(IdMImplementationType.PULL_ACTIONS); - pullActions.setBody(TestPullActions.class.getName()); + pullActions.setType(IdMImplementationType.INBOUND_ACTIONS); + pullActions.setBody(TestInboundActions.class.getName()); Response response = IMPLEMENTATION_SERVICE.create(pullActions); pullActions = IMPLEMENTATION_SERVICE.read( pullActions.getType(), response.getHeaderString(RESTHeaders.RESOURCE_KEY)); @@ -157,13 +157,13 @@ public static void dbPasswordPullActionsSetup() { ImplementationTO pullActions = null; try { pullActions = IMPLEMENTATION_SERVICE.read( - IdMImplementationType.PULL_ACTIONS, DBPasswordPullActions.class.getSimpleName()); + IdMImplementationType.INBOUND_ACTIONS, DBPasswordPullActions.class.getSimpleName()); } catch (SyncopeClientException e) { if (e.getType().getResponseStatus() == Response.Status.NOT_FOUND) { pullActions = new ImplementationTO(); pullActions.setKey(DBPasswordPullActions.class.getSimpleName()); pullActions.setEngine(ImplementationEngine.JAVA); - pullActions.setType(IdMImplementationType.PULL_ACTIONS); + pullActions.setType(IdMImplementationType.INBOUND_ACTIONS); pullActions.setBody(DBPasswordPullActions.class.getName()); Response response = IMPLEMENTATION_SERVICE.create(pullActions); pullActions = IMPLEMENTATION_SERVICE.read( @@ -227,7 +227,7 @@ private void removeTestUsers() { @Test public void getPullActionsClasses() { Set actions = ANONYMOUS_CLIENT.platform(). - getJavaImplInfo(IdMImplementationType.PULL_ACTIONS).orElseThrow().getClasses(); + getJavaImplInfo(IdMImplementationType.INBOUND_ACTIONS).orElseThrow().getClasses(); assertNotNull(actions); assertFalse(actions.isEmpty()); } @@ -245,7 +245,7 @@ public void list() { public void create() { PullTaskTO task = new PullTaskTO(); task.setName("Test create Pull"); - task.setDestinationRealm("/"); + task.setDestinationRealm(SyncopeConstants.ROOT_REALM); task.setResource(RESOURCE_NAME_WS2); task.setPullMode(PullMode.FULL_RECONCILIATION); @@ -502,7 +502,7 @@ public void reconcileFromLDAP() { GroupTO groupTO = matchingGroups.getResult().get(0); assertNotNull(groupTO); assertEquals("testLDAPGroup", groupTO.getName()); - assertTrue(groupTO.getLastChangeContext().contains("PullTask " + task.getKey())); + assertTrue(groupTO.getLastChangeContext().contains("Task " + task.getKey())); assertEquals("true", groupTO.getPlainAttr("show").orElseThrow().getValues().get(0)); assertEquals(matchingUsers.getResult().get(0).getKey(), groupTO.getUserOwner()); assertNull(groupTO.getGroupOwner()); @@ -568,7 +568,7 @@ public void reconcileFromLDAP() { public void reconcileFromScriptedSQL() throws IOException { // 0. reset sync token and set MappingItemTransformer ResourceTO resource = RESOURCE_SERVICE.read(RESOURCE_NAME_DBSCRIPTED); - ResourceTO originalResource = SerializationUtils.clone(resource); + ResourceTO originalResource = RESOURCE_SERVICE.read(RESOURCE_NAME_DBSCRIPTED); Provision provision = resource.getProvision(PRINTER).orElseThrow(); assertNotNull(provision); @@ -751,7 +751,7 @@ public void syncTokenWithErrors() { ResourceTO origResource = RESOURCE_SERVICE.read(RESOURCE_NAME_DBPULL); ConnInstanceTO origConnector = CONNECTOR_SERVICE.read(origResource.getConnector(), null); - ResourceTO resForTest = SerializationUtils.clone(origResource); + ResourceTO resForTest = RESOURCE_SERVICE.read(RESOURCE_NAME_DBPULL); resForTest.setKey("syncTokenWithErrors"); resForTest.setConnector(null); ConnInstanceTO connForTest = SerializationUtils.clone(origConnector); @@ -1166,13 +1166,13 @@ public void issueSYNCOPE258() throws IOException { // ----------------------------- ImplementationTO corrRule = null; try { - corrRule = IMPLEMENTATION_SERVICE.read(IdMImplementationType.PULL_CORRELATION_RULE, "TestPullRule"); + corrRule = IMPLEMENTATION_SERVICE.read(IdMImplementationType.INBOUND_CORRELATION_RULE, "TestPullRule"); } catch (SyncopeClientException e) { if (e.getType().getResponseStatus() == Response.Status.NOT_FOUND) { corrRule = new ImplementationTO(); corrRule.setKey("TestPullRule"); corrRule.setEngine(ImplementationEngine.GROOVY); - corrRule.setType(IdMImplementationType.PULL_CORRELATION_RULE); + corrRule.setType(IdMImplementationType.INBOUND_CORRELATION_RULE); corrRule.setBody(IOUtils.toString( getClass().getResourceAsStream("/TestPullRule.groovy"), StandardCharsets.UTF_8)); Response response = IMPLEMENTATION_SERVICE.create(corrRule); @@ -1183,9 +1183,9 @@ public void issueSYNCOPE258() throws IOException { } assertNotNull(corrRule); - PullPolicyTO policyTO = POLICY_SERVICE.read(PolicyType.PULL, "9454b0d7-2610-400a-be82-fc23cf553dd6"); + InboundPolicyTO policyTO = POLICY_SERVICE.read(PolicyType.INBOUND, "9454b0d7-2610-400a-be82-fc23cf553dd6"); policyTO.getCorrelationRules().put(AnyTypeKind.USER.name(), corrRule.getKey()); - POLICY_SERVICE.update(PolicyType.PULL, policyTO); + POLICY_SERVICE.update(PolicyType.INBOUND, policyTO); // ----------------------------- PullTaskTO task = new PullTaskTO(); @@ -1416,7 +1416,7 @@ public void issueSYNCOPE313LDAP() throws Exception { ImplementationTO pullActions = new ImplementationTO(); pullActions.setKey(LDAPPasswordPullActions.class.getSimpleName()); pullActions.setEngine(ImplementationEngine.JAVA); - pullActions.setType(IdMImplementationType.PULL_ACTIONS); + pullActions.setType(IdMImplementationType.INBOUND_ACTIONS); pullActions.setBody(LDAPPasswordPullActions.class.getName()); Response response = IMPLEMENTATION_SERVICE.create(pullActions); pullActions = IMPLEMENTATION_SERVICE.read( diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ResourceITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ResourceITCase.java index aeab077a638..975ec38bc5c 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ResourceITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/ResourceITCase.java @@ -158,8 +158,7 @@ public void createOverridingProps() { prop.setSchema(schema); prop.getValues().add("http://invalidurl/"); - Set connectorConfigurationProperties = Set.of(prop); - resourceTO.getConfOverride().addAll(connectorConfigurationProperties); + resourceTO.setConfOverride(Optional.of(List.of(prop))); Response response = RESOURCE_SERVICE.create(resourceTO); ResourceTO actual = getObject(response.getLocation(), ResourceService.class, ResourceTO.class); @@ -262,33 +261,30 @@ public void createWithInvalidMapping() { @Test public void createWithoutExtAttr() { - assertThrows(SyncopeClientException.class, () -> { - String resourceKey = RESOURCE_NAME_CREATE_WRONG; - ResourceTO resourceTO = new ResourceTO(); - resourceTO.setKey(resourceKey); - resourceTO.setConnector("5ffbb4ac-a8c3-4b44-b699-11b398a1ba08"); + ResourceTO resourceTO = new ResourceTO(); + resourceTO.setKey(RESOURCE_NAME_CREATE_WRONG); + resourceTO.setConnector("5ffbb4ac-a8c3-4b44-b699-11b398a1ba08"); - Provision provisionTO = new Provision(); - provisionTO.setAnyType(AnyTypeKind.USER.name()); - provisionTO.setObjectClass(ObjectClass.ACCOUNT_NAME); - resourceTO.getProvisions().add(provisionTO); + Provision provisionTO = new Provision(); + provisionTO.setAnyType(AnyTypeKind.USER.name()); + provisionTO.setObjectClass(ObjectClass.ACCOUNT_NAME); + resourceTO.getProvisions().add(provisionTO); - Mapping mapping = new Mapping(); - provisionTO.setMapping(mapping); + Mapping mapping = new Mapping(); + provisionTO.setMapping(mapping); - Item item = new Item(); - item.setIntAttrName("key"); - item.setExtAttrName("userId"); - item.setConnObjectKey(true); - mapping.setConnObjectKeyItem(item); + Item item = new Item(); + item.setIntAttrName("key"); + item.setExtAttrName("userId"); + item.setConnObjectKey(true); + mapping.setConnObjectKeyItem(item); - item = new Item(); - item.setIntAttrName("usernane"); - // missing extAttrName ... - mapping.add(item); + item = new Item(); + item.setIntAttrName("usernane"); + // missing extAttrName ... + mapping.add(item); - createResource(resourceTO); - }); + assertThrows(SyncopeClientException.class, () -> createResource(resourceTO)); } @Test diff --git a/fit/core-reference/src/test/resources/TestPullRule.groovy b/fit/core-reference/src/test/resources/TestPullRule.groovy index 55f6593f40b..f77e425ec73 100644 --- a/fit/core-reference/src/test/resources/TestPullRule.groovy +++ b/fit/core-reference/src/test/resources/TestPullRule.groovy @@ -20,17 +20,17 @@ import groovy.transform.CompileStatic import org.apache.syncope.common.lib.to.Provision import org.apache.syncope.core.persistence.api.dao.search.AttrCond import org.apache.syncope.core.persistence.api.dao.search.SearchCond -import org.apache.syncope.core.provisioning.api.rules.PullCorrelationRule -import org.identityconnectors.framework.common.objects.SyncDelta +import org.apache.syncope.core.provisioning.api.rules.InboundCorrelationRule +import org.identityconnectors.framework.common.objects.LiveSyncDelta /** * Test pull rule relying on {@code email} attribute value. */ @CompileStatic -class TestPullRule implements PullCorrelationRule { +class TestPullRule implements InboundCorrelationRule { @Override - SearchCond getSearchCond(final SyncDelta syncDelta, final Provision provision) { + SearchCond getSearchCond(final LiveSyncDelta syncDelta, final Provision provision) { AttrCond cond = new AttrCond(); cond.setSchema("email"); cond.setType(AttrCond.Type.EQ); diff --git a/pom.xml b/pom.xml index 2d01792096d..a10e45a7f13 100644 --- a/pom.xml +++ b/pom.xml @@ -395,7 +395,7 @@ under the License. 2022-12-26T08:35:28Z ${project.version} - 1.6.0.0-RC1 + 1.6.0.0-SNAPSHOT 1.5.0-RC1 1.1.0-RC1 2.3.0 @@ -408,6 +408,7 @@ under the License. 1.0.3 3.0.5 0.5 + 1.0.0-SNAPSHOT 4.1.0 1.79 @@ -487,6 +488,9 @@ under the License. 2525 1110 + 19092 + account-provisioning,group-provisioning + ${project.build.directory}/test-csvdir 9080 @@ -704,10 +708,6 @@ under the License. org.springframework.boot spring-boot-starter-logging - - org.springframework.boot - spring-boot-starter-tomcat - com.fasterxml.jackson.module jackson-module-parameter-names @@ -735,10 +735,6 @@ under the License. org.springframework.boot spring-boot-starter-logging - - org.springframework.boot - spring-boot-starter-tomcat - @@ -819,10 +815,6 @@ under the License. org.springframework.boot spring-boot-starter-logging - - org.springframework.boot - spring-boot-starter-tomcat - @@ -835,10 +827,6 @@ under the License. org.springframework.boot spring-boot-starter-logging - - org.springframework.boot - spring-boot-starter-tomcat - cglib cglib @@ -1577,6 +1565,12 @@ under the License. ${connid.cmd.version} bundle + + net.tirasa.connid.bundles + net.tirasa.connid.bundles.kafka + ${connid.kafka.version} + bundle + diff --git a/src/main/asciidoc/getting-started/introduction.adoc b/src/main/asciidoc/getting-started/introduction.adoc index 02615746796..b2eaa9cbf5e 100644 --- a/src/main/asciidoc/getting-started/introduction.adoc +++ b/src/main/asciidoc/getting-started/introduction.adoc @@ -129,7 +129,7 @@ for delegated administration. *_Secure Remote Access_* or *_SRA_* is a security-enabled API gateway with HTTP reverse proxying capabilities. *_Core_* is the component providing IdM services and acting as central repository for other components' configuration. + -It exposes a fully-compliant https://en.wikipedia.org/wiki/Java_API_for_RESTful_Web_Services[JAX-RS 2.1^] +It exposes a fully-compliant https://en.wikipedia.org/wiki/Jakarta_RESTful_Web_Services[Jakarta RESTful Web Services 3.1^] https://en.wikipedia.org/wiki/Representational_state_transfer[RESTful^] interface which enables third-party applications, written in any programming language, to consume IdM services. @@ -144,12 +144,12 @@ all-Java implementation can be extended for this purpose. from a provided list - including one based on https://www.flowable.org/[Flowable^], the reference open source http://www.bpmn.org/[BPMN 2.0^] implementations - or define new, custom ones. * *_Persistence_* manages all data (users, groups, attributes, resources, ...) at a high level -using a standard https://en.wikipedia.org/wiki/Java_Persistence_API[JPA 2.2^] approach. The data is persisted to an underlying +using a standard https://en.wikipedia.org/wiki/Jakarta_Persistence[Jakarta Persistence 3.1] approach. The data is persisted to an underlying database, referred to as *_Internal Storage_*. Consistency is ensured via the comprehensive -http://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html[transaction management^] +https://docs.spring.io/spring-framework/reference/6.1/data-access/transaction.html[transaction management^] provided by the Spring Framework. + Globally, this offers the ability to easily scale up to a million entities and at the same time allows great portability with no code -changes: MySQL, MariaDB, PostgreSQL, Oracle and MS SQL Server are fully supported deployment options. +changes: PostgreSQL, MySQL, MariaDB and Oracle are fully supported deployment options. * *_Security_* defines a fine-grained set of entitlements which can be granted to administrators, thus enabling the implementation of delegated administration scenarios. diff --git a/src/main/asciidoc/getting-started/obtain.adoc b/src/main/asciidoc/getting-started/obtain.adoc index a345144dd4a..92a56965b0a 100644 --- a/src/main/asciidoc/getting-started/obtain.adoc +++ b/src/main/asciidoc/getting-started/obtain.adoc @@ -174,7 +174,7 @@ services: - "18080:8080" restart: always environment: - SPRING_PROFILES_ACTIVE: docker,postgresql + SPRING_PROFILES_ACTIVE: docker,postgresql,saml2 DB_URL: jdbc:postgresql://db:5432/syncope?stringtype=unspecified DB_USER: syncope DB_PASSWORD: syncope @@ -196,7 +196,7 @@ services: - "28080:8080" restart: always environment: - SPRING_PROFILES_ACTIVE: docker + SPRING_PROFILES_ACTIVE: docker,saml2 KEYMASTER_ADDRESS: https://syncope:8080/syncope/rest/keymaster KEYMASTER_USERNAME: ${ANONYMOUS_USER} KEYMASTER_PASSWORD: ${ANONYMOUS_KEY} @@ -212,7 +212,7 @@ services: - "38080:8080" restart: always environment: - SPRING_PROFILES_ACTIVE: docker + SPRING_PROFILES_ACTIVE: docker,saml2 KEYMASTER_ADDRESS: https://syncope:8080/syncope/rest/keymaster KEYMASTER_USERNAME: ${ANONYMOUS_USER} KEYMASTER_PASSWORD: ${ANONYMOUS_KEY} @@ -259,7 +259,7 @@ services: - "18080:8080" restart: always environment: - SPRING_PROFILES_ACTIVE: docker,postgresql + SPRING_PROFILES_ACTIVE: docker,postgresql,saml2 DB_URL: jdbc:postgresql://db:5432/syncope?stringtype=unspecified DB_USER: syncope DB_PASSWORD: syncope @@ -282,7 +282,7 @@ services: - "28080:8080" restart: always environment: - SPRING_PROFILES_ACTIVE: docker + SPRING_PROFILES_ACTIVE: docker,saml2 KEYMASTER_ADDRESS: keymaster:2181 KEYMASTER_USERNAME: ${KEYMASTER_USERNAME:-} KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD:-} @@ -299,7 +299,7 @@ services: - "38080:8080" restart: always environment: - SPRING_PROFILES_ACTIVE: docker + SPRING_PROFILES_ACTIVE: docker,saml2 KEYMASTER_ADDRESS: keymaster:2181 KEYMASTER_USERNAME: ${KEYMASTER_USERNAME:-} KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD:-} @@ -316,7 +316,7 @@ services: - "48080:8080" restart: always environment: - SPRING_PROFILES_ACTIVE: docker + SPRING_PROFILES_ACTIVE: docker,saml2 KEYMASTER_ADDRESS: keymaster:2181 KEYMASTER_USERNAME: ${KEYMASTER_USERNAME:-} KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD:-} @@ -334,7 +334,7 @@ services: - "58080:8080" restart: always environment: - SPRING_PROFILES_ACTIVE: docker + SPRING_PROFILES_ACTIVE: docker,saml2 KEYMASTER_ADDRESS: keymaster:2181 KEYMASTER_USERNAME: ${KEYMASTER_USERNAME:-} KEYMASTER_PASSWORD: ${KEYMASTER_PASSWORD:-} @@ -665,6 +665,9 @@ You can configure any LDAP client (such as http://jxplorer.org/[JXplorer^], for Set `sa` as password + Click 'Connect' button +| External resource: Apache Kafka +| Broker listening at localhost:19092 + |=== ==== Docker Mode diff --git a/src/main/asciidoc/getting-started/systemRequirements.adoc b/src/main/asciidoc/getting-started/systemRequirements.adoc index fb65618e547..7000febe044 100644 --- a/src/main/asciidoc/getting-started/systemRequirements.adoc +++ b/src/main/asciidoc/getting-started/systemRequirements.adoc @@ -38,7 +38,7 @@ Apache Syncope {docVersion} is verified with the following Java EE containers: . https://tomcat.apache.org/download-10.cgi[Apache Tomcat 10^] . https://www.payara.fish/[Payara Server 6^] - . https://www.wildfly.org/[Wildfly 33^] + . https://www.wildfly.org/[Wildfly 34^] === Internal Storage diff --git a/src/main/asciidoc/images/accessibility-console01.png b/src/main/asciidoc/images/accessibility-console01.png deleted file mode 100644 index 1efaa087659..00000000000 Binary files a/src/main/asciidoc/images/accessibility-console01.png and /dev/null differ diff --git a/src/main/asciidoc/reference-guide/architecture/core.adoc b/src/main/asciidoc/reference-guide/architecture/core.adoc index e6711465c98..3d04e5bb050 100644 --- a/src/main/asciidoc/reference-guide/architecture/core.adoc +++ b/src/main/asciidoc/reference-guide/architecture/core.adoc @@ -33,7 +33,7 @@ The rich pre-defined set of endpoints can be <> by adding n given Apache Syncope deployment to complement the native features with domain-specific operations. At a technical level, the RESTful interface is a fully-compliant -https://en.wikipedia.org/wiki/Java_API_for_RESTful_Web_Services[JAX-RS 2.1^] implementation based on +https://en.wikipedia.org/wiki/Jakarta_RESTful_Web_Services[Jakarta RESTful Web Services 3.1^] implementation based on http://cxf.apache.org[Apache CXF^], natively dealing either with JSON, YAML and XML payloads. More details are available in the dedicated <> section. @@ -80,14 +80,14 @@ https://camunda.org/[Camunda^] or http://jbpm.jboss.org/[jBPM^], can be written ==== Persistence All data (users, groups, attributes, resources, ...) is internally managed at a high level using a standard -https://en.wikipedia.org/wiki/Java_Persistence_API[JPA 2.2^] approach based on https://openjpa.apache.org[Apache OpenJPA^]. +https://en.wikipedia.org/wiki/Jakarta_Persistence[Jakarta Persistence 3.1] approach based on https://openjpa.apache.org[Apache OpenJPA^]. The data is persisted into an underlying database, referred to as *_Internal Storage_*. Consistency is ensured via the comprehensive -https://docs.spring.io/spring-framework/docs/5.3.x/reference/html/data-access.html#transaction[transaction management^] +https://docs.spring.io/spring-framework/reference/6.1/data-access/transaction.html[transaction management^] provided by the Spring Framework. Globally, this offers the ability to easily scale up to a million entities and at the same time allows great portability -with no code changes: MySQL, MariaDB, PostgreSQL, Oracle and MS SQL Server are fully supported +with no code changes: PostgreSQL, MySQL, MariaDB and Oracle are fully supported <>. <> allow to manage data belonging to different https://en.wikipedia.org/wiki/Multitenancy[tenants^] into diff --git a/src/main/asciidoc/reference-guide/concepts/entitlements.adoc b/src/main/asciidoc/reference-guide/concepts/entitlements.adoc index 58429008435..8ac00aa43c9 100644 --- a/src/main/asciidoc/reference-guide/concepts/entitlements.adoc +++ b/src/main/asciidoc/reference-guide/concepts/entitlements.adoc @@ -30,7 +30,7 @@ ifeval::["{snapshotOrRelease}" == "snapshot"] https://github.com/apache/syncope/blob/master/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java[RealmLogic^] endif::[] , the -https://docs.spring.io/spring-security/site/docs/5.5.x/reference/html5/#el-common-built-in[`hasRole` expression^] +https://docs.spring.io/spring-security/reference/6.3/servlet/authorization/method-security.html#authorization-expressions[`hasRole` expression^] is used together with one of the standard entitlements to restrict access only to Users owning the `REALM_SEARCH` entitlement. diff --git a/src/main/asciidoc/reference-guide/concepts/externalresources.adoc b/src/main/asciidoc/reference-guide/concepts/externalresources.adoc index 952193fcb84..2a71a41b45e 100644 --- a/src/main/asciidoc/reference-guide/concepts/externalresources.adoc +++ b/src/main/asciidoc/reference-guide/concepts/externalresources.adoc @@ -35,9 +35,11 @@ Several Connector Bundles come included with Apache Syncope: * https://connid.atlassian.net/wiki/pages/viewpage.action?pageId=360482[Active Directory^] * https://connid.atlassian.net/wiki/display/BASE/Azure[Azure^] +* https://connid.atlassian.net/wiki/display/BASE/CMD[CMD ^] * https://connid.atlassian.net/wiki/display/BASE/CSV+Directory[CSV Directory^] * https://connid.atlassian.net/wiki/display/BASE/Database[Database^] * https://connid.atlassian.net/wiki/display/BASE/Google+Apps[Google Apps^] +* https://connid.atlassian.net/wiki/display/BASE/Kafka[Apache Kafka^] * https://connid.atlassian.net/wiki/display/BASE/LDAP[LDAP^] * https://connid.atlassian.net/wiki/display/BASE/Okta[Okta^] * https://connid.atlassian.net/wiki/display/BASE/REST[Scripted REST^] @@ -74,6 +76,7 @@ action is performed on the underlying connector; the capabilities are: `FULL RECONCILIATION` or `FILTERED RECONCILIATION` <> ** `SYNC` - synchronize objects from the underlying connector; used during <> with `INCREMENTAL` <> +** `LIVE_SYNC` - synchronize objects by subscribing to queue systems; used during <> [TIP] .Configuration and capability override @@ -92,17 +95,18 @@ Given a selected connector instance, the following information is required to de * priority - integer value, in use by the default <> * propagation actions - which <> shall be executed during propagation * trace levels - control how much tracing (including logs and execution details) shall be carried over during -<>, <> and <> -* configuration - see <> -* capabilities - see <> -* account policy - which <> to enforce on Users, Groups and Any Objects assigned to +<>, <>, <> and +<> +* <> +* <> +* <> to enforce on Users, Groups and Any Objects assigned to this external resource -* password policy - which <> to enforce on Users, Groups and Any Objects assigned to +* <> to enforce on Users, Groups and Any Objects assigned to this external resource -* pull policy - which <> to apply during <> on this external -resource -* push policy - which <> to apply during <> on this external -resource +* <> to apply during <> on this external resource +* <> to apply during <> or +<> on this external resource +* <> to apply during <> on this external resource ==== Mapping @@ -146,7 +150,7 @@ endif::[] this mapping item must be necessarily available or not; compared to a simple boolean value, such condition allows complex statements to be expressed such as 'be mandatory only if this other attribute value is above 14', and so on * remote key flag - should this item be considered as the key value on the Identity Store, if no -<> or <> correlation rules are applicable? +<> or <> correlation rules are applicable? * password flag (Users only) - should this item be treated as the password value? * purpose - should this item be considered for <> / <>, <>, both or none? @@ -237,7 +241,7 @@ attribute values override, with reference to the owning User. Linked Accounts are propagated alongside with owning User - following the existing <> if available - and pulled according to the given -<>, if present. +<>, if present. [.text-center] image::linked_accounts.png[title="Linked Accounts",alt="Linked Accounts"] diff --git a/src/main/asciidoc/reference-guide/concepts/notifications.adoc b/src/main/asciidoc/reference-guide/concepts/notifications.adoc index 68f89d44c2d..d25d606aad9 100644 --- a/src/main/asciidoc/reference-guide/concepts/notifications.adoc +++ b/src/main/asciidoc/reference-guide/concepts/notifications.adoc @@ -95,7 +95,7 @@ successful execution of the event identified by the `unexpected identification` [NOTE] ==== Custom events can be used to trigger notifications from non-predefined joint points, as BPMN `userTask` -instances within the <>, <>, <>, <> or +instances within the <>, <>, <>, <> or other custom code. ==== diff --git a/src/main/asciidoc/reference-guide/concepts/policies.adoc b/src/main/asciidoc/reference-guide/concepts/policies.adoc index c9d4a1af871..488015930a0 100644 --- a/src/main/asciidoc/reference-guide/concepts/policies.adoc +++ b/src/main/asciidoc/reference-guide/concepts/policies.adoc @@ -116,7 +116,7 @@ endif::[] * maximum length - the maximum length to allow; `0` means no limit set; * minimum length - the minimum length to allow; `0` means no limit set; -* pattern - https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/regex/Pattern.html[Java regular expression pattern^] to +* pattern - https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/regex/Pattern.html[Java regular expression pattern^] to match; `NULL` means no match is attempted; * all uppercase - are lowercase characters allowed? * all lowercase - are uppercase characters allowed? @@ -381,10 +381,10 @@ attributes will be instead propagated ** `EXPONENTIAL` - increases the back off period for each retry attempt in a given set up to a limit ** `RANDOM` - chooses a random multiple of the interval that would come from a simple deterministic exponential -[[policies-pull]] -==== Pull +[[policies-inbound]] +==== Inbound -Pull policies are evaluated during the execution of <> and are meant to: +Inbound policies are evaluated during the execution of <> and are meant to: . help match existing Users, Groups and Any Objects during <>, thus generating update events (rather than create) @@ -393,10 +393,10 @@ can be mapped to two distinct Users in Apache Syncope?) [NOTE] ==== -When set for resource R, a pull policy is enforced on all Users, Groups and Any Objects pulled from R. +When set for resource R, an inbound policy is enforced on all Users, Groups and Any Objects pulled from R. ==== -When defining a pull policy, the following information must be provided: +When defining an inbound policy, the following information must be provided: * conflict resolution action ** `IGNORE` - do nothing @@ -406,29 +406,29 @@ When defining a pull policy, the following information must be provided: * rules - set of correlation rules to evaluate with the current policy; for each defined <>, a different rule is required -===== Pull Correlation Rules +===== Inbound Correlation Rules -Pull correlation rules define how to match objects received from <> +Inbound correlation rules define how to match objects received from <> with existing Users (including <>), Groups or Any Objects. The ifeval::["{snapshotOrRelease}" == "release"] -https://github.com/apache/syncope/blob/syncope-{docVersion}/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultPullCorrelationRule.java[default^] +https://github.com/apache/syncope/blob/syncope-{docVersion}/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultInboundCorrelationRule.java[default^] endif::[] ifeval::["{snapshotOrRelease}" == "snapshot"] -https://github.com/apache/syncope/blob/master/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultPullCorrelationRule.java[default^] +https://github.com/apache/syncope/blob/master/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultInboundCorrelationRule.java[default^] endif::[] implementation attempts to match entities on the basis of the values of the provided plain attributes, according to the available <>. [TIP] ==== -Custom pull correlation rules can be provided by <> the +Custom inbound correlation rules can be provided by <> the ifeval::["{snapshotOrRelease}" == "release"] -https://github.com/apache/syncope/blob/syncope-{docVersion}/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/rules/PullCorrelationRule.java[PullCorrelationRule^] +https://github.com/apache/syncope/blob/syncope-{docVersion}/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/rules/InboundCorrelationRule.java[InboundCorrelationRule^] endif::[] ifeval::["{snapshotOrRelease}" == "snapshot"] -https://github.com/apache/syncope/blob/master/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/rules/PullCorrelationRule.java[PullCorrelationRule^] +https://github.com/apache/syncope/blob/master/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/rules/InboundCorrelationRule.java[InboundCorrelationRule^] endif::[] interface. ==== diff --git a/src/main/asciidoc/reference-guide/concepts/provisioning/livesync.adoc b/src/main/asciidoc/reference-guide/concepts/provisioning/livesync.adoc new file mode 100644 index 00000000000..d86502934e3 --- /dev/null +++ b/src/main/asciidoc/reference-guide/concepts/provisioning/livesync.adoc @@ -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. +// +[[provisioning-livesync]] +==== Live Sync + +Live sync allows to acquire identity data from records published to queue systems, like as +https://kafka.apache.org/[Apache Kafka^], https://activemq.apache.org/[Apache ActiveMQ^], +https://cloud.google.com/pubsub/[Google PubSub^] or similar. + +Compared to <>, records are processed as soon as they are published in the queue system, +while the <> is running. + +For each external resource, a single <> can be defined: once started, it will remain +active until stopped. + +Live sync tasks will be triggered by the publication of matching records on the external resource for all +<> <>, sorted according to the order defined by a custom implementation of +ifeval::["{snapshotOrRelease}" == "release"] +https://github.com/apache/syncope/blob/syncope-{docVersion}/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ProvisionSorter.java[ProvisionSorter^] +endif::[] +ifeval::["{snapshotOrRelease}" == "snapshot"] +https://github.com/apache/syncope/blob/master/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/ProvisionSorter.java[ProvisionSorter^] +endif::[] +or its default implementation +ifeval::["{snapshotOrRelease}" == "release"] +https://github.com/apache/syncope/blob/syncope-{docVersion}/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultProvisionSorter.java[DefaultProvisionSorter^] +endif::[] +ifeval::["{snapshotOrRelease}" == "snapshot"] +https://github.com/apache/syncope/blob/master/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultProvisionSorter.java[DefaultProvisionSorter^] +endif::[] +. + +Once a record is received, the configured <> of +ifeval::["{snapshotOrRelease}" == "release"] +https://github.com/apache/syncope/blob/syncope-{docVersion}/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/LiveSyncDeltaMapper.java[LiveSyncDeltaMapper^] +endif::[] +ifeval::["{snapshotOrRelease}" == "snapshot"] +https://github.com/apache/syncope/blob/master/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/LiveSyncDeltaMapper.java[LiveSyncDeltaMapper^] +endif::[] +is invoked to transform the record into a format which is in turn provided to an internally created and processed +<>. diff --git a/src/main/asciidoc/reference-guide/concepts/provisioning/propagation.adoc b/src/main/asciidoc/reference-guide/concepts/provisioning/propagation.adoc index 8c9b8bae3cf..a8b78f60323 100644 --- a/src/main/asciidoc/reference-guide/concepts/provisioning/propagation.adoc +++ b/src/main/asciidoc/reference-guide/concepts/provisioning/propagation.adoc @@ -22,7 +22,8 @@ Whenever a change is performed via REST on Users, Groups or Any Objects: . a set of <> is generated, one for each associated external resource; . the generated propagation tasks are executed, e.g. the corresponding operations (create, update or delete) are sent -out, via connectors, to the configured Identity Stores; the tasks can be saved for later re-execution. +out, via connectors, to the configured Identity Stores, according to the configured +<>, if defined; the tasks can be saved for later re-execution. [[propagation-resources]] [TIP] @@ -97,6 +98,9 @@ but sound perfectly understandable once explained; in particular: * an `UPDATE` propagation task might result in an effective `CREATE` sent to the Connector + if preliminary read does not find any external object matching the remote key of the objected requested to be updated + +Please also note that this behavior is affected by the configured <>, if +available: in particular, whether fetching around provisioning is enabled or not. ==== Different implementations of the diff --git a/src/main/asciidoc/reference-guide/concepts/provisioning/provisioning.adoc b/src/main/asciidoc/reference-guide/concepts/provisioning/provisioning.adoc index 7e71ae284c9..a2ef2d643e7 100644 --- a/src/main/asciidoc/reference-guide/concepts/provisioning/provisioning.adoc +++ b/src/main/asciidoc/reference-guide/concepts/provisioning/provisioning.adoc @@ -89,6 +89,8 @@ include::propagation.adoc[] include::pull.adoc[] +include::livesync.adoc[] + include::push.adoc[] include::passwordreset.adoc[] diff --git a/src/main/asciidoc/reference-guide/concepts/provisioning/pull.adoc b/src/main/asciidoc/reference-guide/concepts/provisioning/pull.adoc index d56219bca51..89a8bbff382 100644 --- a/src/main/asciidoc/reference-guide/concepts/provisioning/pull.adoc +++ b/src/main/asciidoc/reference-guide/concepts/provisioning/pull.adoc @@ -42,7 +42,7 @@ endif::[] Each entity is then processed in an isolated transaction; a retrieved entity can be: . _matching_ if a corresponding internal entity was found, according to the <> of - or the -<> set for, if present - the enclosing external resource; +<> set for, if present - the enclosing external resource; . _unmatching_ otherwise. Once this has been assessed, entities are processed according to the matching / unmatching rules specified for the pull task: @@ -102,15 +102,15 @@ A typical use case is, when pulling Users from the external resource `R`, to aut further modification in Apache Syncope to such Users will be <> back to `R`. ==== -===== PullActions +===== InboundActions The pull process can be decorated with custom logic to be invoked around task execution, by associating pull tasks to one or more <> of the ifeval::["{snapshotOrRelease}" == "release"] -https://github.com/apache/syncope/blob/syncope-{docVersion}/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/PullActions.java[PullActions^] +https://github.com/apache/syncope/blob/syncope-{docVersion}/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/InboundActions.java[InboundActions^] endif::[] ifeval::["{snapshotOrRelease}" == "snapshot"] -https://github.com/apache/syncope/blob/master/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/PullActions.java[PullActions^] +https://github.com/apache/syncope/blob/master/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/InboundActions.java[InboundActions^] endif::[] interface. @@ -159,6 +159,16 @@ endif::[] the cipher algorithm associated with the password must match the value of `Password cipher algorithm` for the https://connid.atlassian.net/wiki/display/BASE/Database+Table#DatabaseTable-ConfigurationProperties[DatabaseTable connector bundle^]. +| +ifeval::["{snapshotOrRelease}" == "release"] +https://github.com/apache/syncope/blob/syncope-{docVersion}/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/KafkaInboundActions.java[KafkaInboundActions^] +endif::[] +ifeval::["{snapshotOrRelease}" == "snapshot"] +https://github.com/apache/syncope/blob/master/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/KafkaInboundActions.java[KafkaInboundActions^] +endif::[] +| Instructs to fetch the attributes required during the <> process for the +https://connid.atlassian.net/wiki/display/BASE/Kafka[Apache Kafka connector bundle^]. + |=== ===== Remediation diff --git a/src/main/asciidoc/reference-guide/concepts/realms.adoc b/src/main/asciidoc/reference-guide/concepts/realms.adoc index 2ad887fea24..bf06dd9baf4 100644 --- a/src/main/asciidoc/reference-guide/concepts/realms.adoc +++ b/src/main/asciidoc/reference-guide/concepts/realms.adoc @@ -25,17 +25,19 @@ Each realm: . has a unique name and a parent realm - except for the pre-defined _root realm_, which is named `/`; . is either a leaf or root of a sub-tree of realms; -. is uniquely identified by the path from the root realm, e.g. `/a/b/c` identifies the sub-realm `c` in the sub-tree rooted -at `b`, having in turn `a` as parent realm, directly under the root realm; -. optionally refers to <> and <> policies: such policies are -enforced on all Users, Groups and Any Objects in the given realm and sub-realms, unless some sub-realms define their own policies. +. is uniquely identified by the path from the root realm, e.g. `/a/b/c` identifies the sub-realm `c` in the sub-tree +rooted at `b`, having in turn `a` as parent realm, directly under the root realm; +. optionally refers to various <> that are enforced on all Users, Groups and Any Objects in the given +realm and sub-realms, unless some sub-realms define their own policies. +. optionally refers to <> +. optionally defines <> -If Users, Groups and Any Objects are members of a realm then they are also members of the parent realm: as a result, the root -realm contains everything, and other realms can be seen as containers that split up the total number of entities into -smaller pools. +If Users, Groups and Any Objects are members of a realm then they are also members of the parent realm: as a result, the +root realm contains everything, and other realms can be seen as containers that split up the total number of entities +into smaller pools. This partition allows fine-grained control over policy enforcement and, alongside with -<> and <>, helps to implement +<> and <>, helps to implement <>. [[dynamic-realms]] @@ -50,6 +52,7 @@ assignment, group membership or any other condition available, with purpose of g <> rights. **** +[[logic-templates]] [TIP] .Logic Templates ==== diff --git a/src/main/asciidoc/reference-guide/concepts/reports.adoc b/src/main/asciidoc/reference-guide/concepts/reports.adoc index 7fd4e596912..726c45b22a3 100644 --- a/src/main/asciidoc/reference-guide/concepts/reports.adoc +++ b/src/main/asciidoc/reference-guide/concepts/reports.adoc @@ -34,4 +34,4 @@ endif::[] providing the custom logic to extract information from Syncope and generate output according to the configured mime type * scheduling information: ** when to start -** https://docs.spring.io/spring-framework/reference/integration/scheduling.html#scheduling-cron-expression[cron expression^] +** https://docs.spring.io/spring-framework/reference/6.1/integration/scheduling.html#scheduling-cron-expression[cron expression^] diff --git a/src/main/asciidoc/reference-guide/concepts/routes.adoc b/src/main/asciidoc/reference-guide/concepts/routes.adoc index b7bfa222989..ff62175562d 100644 --- a/src/main/asciidoc/reference-guide/concepts/routes.adoc +++ b/src/main/asciidoc/reference-guide/concepts/routes.adoc @@ -50,7 +50,7 @@ The received response, after being post-processed by matching route's _filters_, ==== Predicates Inside Route definition, each predicate will be referring to some Spring Cloud Gateway's -https://docs.spring.io/spring-cloud-gateway/docs/3.1.x/reference/html/#gateway-request-predicates-factories[Predicate factory^]: +https://docs.spring.io/spring-cloud-gateway/docs/4.1.x/reference/html/#gateway-request-predicates-factories[Predicate factory^]: * `AFTER` matches requests that happen after the specified datetime; * `BEFORE` matches requests that happen before the specified datetime; @@ -74,7 +74,7 @@ endif::[] ==== Filters Inside Route definition, each filter will be referring to some Spring Cloud Gateway's -https://docs.spring.io/spring-cloud-gateway/docs/3.1.x/reference/html/#gatewayfilter-factories[Filter factory^]: +https://docs.spring.io/spring-cloud-gateway/docs/4.1.x/reference/html/#gatewayfilter-factories[Filter factory^]: * `ADD_REQUEST_HEADER` adds a header to the downstream request's headers; * `ADD_REQUEST_PARAMETER` adds a parameter too the downstream request's query string; diff --git a/src/main/asciidoc/reference-guide/concepts/tasks.adoc b/src/main/asciidoc/reference-guide/concepts/tasks.adoc index d6f08879577..a9983bb6310 100644 --- a/src/main/asciidoc/reference-guide/concepts/tasks.adoc +++ b/src/main/asciidoc/reference-guide/concepts/tasks.adoc @@ -97,11 +97,11 @@ When defining a pull task, the following information must be provided: * whether <> is enabled * whether to synchronize the status information from the related identity store * selected <> -* optional <> +* optional <> * <> * scheduling information: ** when to start -** https://docs.spring.io/spring-framework/reference/integration/scheduling.html#scheduling-cron-expression[cron expression^] +** https://docs.spring.io/spring-framework/reference/6.1/integration/scheduling.html#scheduling-cron-expression[cron expression^] [NOTE] ==== @@ -134,6 +134,45 @@ Resource; it is also possible to configure a pull task to work on several object overall execution time. ==== +[[tasks-livesync]] +==== Live Sync + +Live sync tasks are required to define and trigger the <> process from Identity Stores. + +When defining a live sync task, the following information must be provided: + +* related <> +* destination <> - where entities selected for creation are going to be placed +* whether creation, update or deletion on internal storage are allowed or not +* whether <> is enabled +* whether to synchronize the status information from the related identity store +* selected <> +* selected <> +* optional <> +* <> + +[NOTE] +==== +Live sync tasks are executed via the +ifeval::["{snapshotOrRelease}" == "release"] +https://github.com/apache/syncope/blob/syncope-{docVersion}/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LiveSyncJobDelegate.java[LiveSyncJobDelegate^] +endif::[] +ifeval::["{snapshotOrRelease}" == "snapshot"] +https://github.com/apache/syncope/blob/master/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/LiveSyncJobDelegate.java[LiveSyncJobDelegate^] +endif::[] +during the <> process; the execution results are permanently saved - for examining the +execution details - depending on the trace level set on the related <>. +==== + +[[concurrent-tasks-livesync]] +[TIP] +.Concurrent Live Sync Task Executions +==== +By default, live sync tasks are set to accept and sequentially process the objects received from the configured External +Resource; it is also possible to configure a live sync task to work on several objects at once in order to speed up the +overall execution time. +==== + [[tasks-push]] ==== Push @@ -150,7 +189,7 @@ When defining a push task, the following information must be provided: * optional <> * scheduling information: ** when to start -** https://docs.spring.io/spring-framework/reference/integration/scheduling.html#scheduling-cron-expression[cron expression^] +** https://docs.spring.io/spring-framework/reference/6.1/integration/scheduling.html#scheduling-cron-expression[cron expression^] [NOTE] ==== @@ -219,7 +258,7 @@ When defining a macro task, the following information must be provided: list, update or execute the given macro task * scheduling information: ** when to start -** https://docs.spring.io/spring-framework/reference/integration/scheduling.html#scheduling-cron-expression[cron expression^] +** https://docs.spring.io/spring-framework/reference/6.1/integration/scheduling.html#scheduling-cron-expression[cron expression^] ===== MacroActions @@ -250,7 +289,7 @@ endif::[] providing the custom logic to execute * scheduling information: ** when to start -** https://docs.spring.io/spring-framework/reference/integration/scheduling.html#scheduling-cron-expression[cron expression^] +** https://docs.spring.io/spring-framework/reference/6.1/integration/scheduling.html#scheduling-cron-expression[cron expression^] [TIP] ==== diff --git a/src/main/asciidoc/reference-guide/concepts/typemanagement.adoc b/src/main/asciidoc/reference-guide/concepts/typemanagement.adoc index 77e6da1151a..daf6de46896 100644 --- a/src/main/asciidoc/reference-guide/concepts/typemanagement.adoc +++ b/src/main/asciidoc/reference-guide/concepts/typemanagement.adoc @@ -38,17 +38,17 @@ When defining a plain schema, the following information must be provided: * Type ** `String` ** `Long` - allows to specify a _conversion pattern_ to / from string, according to -https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/text/DecimalFormat.html[DecimalFormat^] +https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/text/DecimalFormat.html[DecimalFormat^] ** `Double` - allows to specify a _conversion pattern_ to / from string, according to -https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/text/DecimalFormat.html[DecimalFormat^] +https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/text/DecimalFormat.html[DecimalFormat^] ** `Boolean` ** `Date` - allows to specify a _conversion pattern_ to / from string, according to -https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/text/DateFormat.html[DateFormat^] +https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/text/DateFormat.html[DateFormat^] ** `Enum` - allows to specify which predetermined value(s) can be selected ** `Dropdown` - allows to specify an <> which will dynamically return the value(s) that can be selected ** `Encrypted` -*** secret key (stored or referenced as https://docs.spring.io/spring-framework/docs/5.3.x/reference/html/core.html#beans-property-source-abstraction[Spring property^]) +*** secret key (stored or referenced as https://docs.spring.io/spring-framework/reference/6.1/core/beans/environment.html#beans-using-propertysource[Spring property^]) *** cipher algorithm *** whether transparent encryption is to be enabled, e.g. attribute values are stored as encrypted but available as cleartext on-demand (requires AES ciphering) diff --git a/src/main/asciidoc/reference-guide/configuration/configurationparameters.adoc b/src/main/asciidoc/reference-guide/configuration/configurationparameters.adoc index 1eb6011bcad..cc8bab7ec9b 100644 --- a/src/main/asciidoc/reference-guide/configuration/configurationparameters.adoc +++ b/src/main/asciidoc/reference-guide/configuration/configurationparameters.adoc @@ -34,7 +34,7 @@ at runtime, expecially with high-availability. * `jwt.lifetime.minutes` - validity of https://en.wikipedia.org/wiki/JSON_Web_Token[JSON Web Token^] values used for <> (in minutes); * `notificationjob.cronExpression` - -https://docs.spring.io/spring-framework/reference/integration/scheduling.html#scheduling-cron-expression[cron^] expression describing how +https://docs.spring.io/spring-framework/reference/6.1/integration/scheduling.html#scheduling-cron-expression[cron^] expression describing how frequently the pending <> are processed: empty means disabled; [NOTE] Restarting the deployment is required when changing value for this parameter. diff --git a/src/main/asciidoc/reference-guide/configuration/dbms.adoc b/src/main/asciidoc/reference-guide/configuration/dbms.adoc index 4e1099092f1..9a7cfb5bd58 100644 --- a/src/main/asciidoc/reference-guide/configuration/dbms.adoc +++ b/src/main/asciidoc/reference-guide/configuration/dbms.adoc @@ -40,7 +40,7 @@ persistence.domain[0].poolMinIdle=5 as `core/src/main/resources/core-postgresql.properties`. Do not forget to include `postgresql` as -https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.profiles.adding-active-profiles[Spring Boot profile^] +https://docs.spring.io/spring-boot/3.3/reference/features/profiles.html#features.profiles.adding-active-profiles[Spring Boot profile^] for the Core application. [WARNING] @@ -73,7 +73,7 @@ persistence.domain[0].poolMinIdle=5 as `core/src/main/resources/core-mysql.properties`. Do not forget to include `mysql` as -https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.profiles.adding-active-profiles[Spring Boot profile^] +https://docs.spring.io/spring-boot/3.3/reference/features/profiles.html#features.profiles.adding-active-profiles[Spring Boot profile^] for the Core application. [CAUTION] @@ -110,7 +110,7 @@ persistence.domain[0].poolMinIdle=5 as `core/src/main/resources/core-mariadb.properties`. Do not forget to include `mariadb` as -https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.profiles.adding-active-profiles[Spring Boot profile^] +https://docs.spring.io/spring-boot/3.3/reference/features/profiles.html#features.profiles.adding-active-profiles[Spring Boot profile^] for the Core application. [CAUTION] @@ -159,7 +159,7 @@ persistence.domain[0].poolMinIdle=5 as `core/src/main/resources/core-oracle.properties`. Do not forget to include `oracle` as -https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.profiles.adding-active-profiles[Spring Boot profile^] +https://docs.spring.io/spring-boot/3.3/reference/features/profiles.html#features.profiles.adding-active-profiles[Spring Boot profile^] for the Core application. [WARNING] diff --git a/src/main/asciidoc/reference-guide/configuration/deployment.adoc b/src/main/asciidoc/reference-guide/configuration/deployment.adoc index 8a2bc6e41ce..d971b85fcce 100644 --- a/src/main/asciidoc/reference-guide/configuration/deployment.adoc +++ b/src/main/asciidoc/reference-guide/configuration/deployment.adoc @@ -24,7 +24,7 @@ Java EE containers. [WARNING] The only exception is <> that, being based on Spring Cloud Gateway - which in turn is built on -https://docs.spring.io/spring-framework/docs/5.3.x/reference/html/web-reactive.html[Spring WebFlux^] and +https://docs.spring.io/spring-framework/reference/6.1/web-reactive.html[Spring WebFlux^] and https://projectreactor.io/docs[Project Reactor^], is only available as standalone application. [CAUTION] @@ -51,7 +51,7 @@ applications as standalone fat JAR or WAR files. [TIP] Spring Boot applications can also be -https://docs.spring.io/spring-boot/docs/current/reference/html/deployment.html#deployment.installing[installed as system services^]. +https://docs.spring.io/spring-boot/3.3/how-to/deployment/installing.html[installed as system services^]. .Run Core application as standalone under GNU / Linux ==== @@ -71,7 +71,7 @@ $ java -Dsyncope.conf.dir=/opt/syncope/conf \ -jar /opt/syncope/lib/syncope.war ---- Further options can be passed to last command, according to Spring Boot -https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#appendix.application-properties[documentation^]; +https://docs.spring.io/spring-boot/3.3/appendix/application-properties/index.html[documentation^]; for example: * `--spring.config.additional-location=/path` + @@ -103,7 +103,7 @@ for the Master domain. Each JavaEE Container provides its own way to accomplish this task: * https://tomcat.apache.org/tomcat-10.0-doc/jdbc-pool.html[Apache Tomcat 10^] * https://docs.payara.fish/community/docs/Technical%20Documentation/Payara%20Server%20Documentation/Server%20Configuration%20And%20Management/JDBC%20Resource%20Management/JDBC.html[Payara Server 6^] - * https://docs.wildfly.org/33/Admin_Guide.html#DataSource[Wildfly 33^] + * https://docs.wildfly.org/34/Admin_Guide.html#DataSource[Wildfly 34^] **** ==== Apache Tomcat 10 @@ -200,7 +200,7 @@ For better performance under GNU / Linux, do not forget to include the system pr .... ==== -==== Wildfly 33 +==== Wildfly 34 Add @@ -256,6 +256,10 @@ with org.apache.tomcat.embed tomcat-embed-el + + org.springframework.boot + spring-boot-starter-tomcat + .... @@ -279,7 +283,7 @@ javadocPaths=/WEB-INF/lib/syncope-common-idrepo-rest-api-${syncope.version}-java as `core/src/main/resources/core-wildfy.properties`. Do not forget to include `widlfly` as -https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.profiles.adding-active-profiles[Spring Boot profile^] +https://docs.spring.io/spring-boot/3.3/reference/features/profiles.html[Spring Boot profile^] for the Core application. [WARNING] diff --git a/src/main/asciidoc/reference-guide/usage/actuator.adoc b/src/main/asciidoc/reference-guide/usage/actuator.adoc index 7049df1cbe8..672f845903a 100644 --- a/src/main/asciidoc/reference-guide/usage/actuator.adoc +++ b/src/main/asciidoc/reference-guide/usage/actuator.adoc @@ -23,7 +23,7 @@ Spring Boot's actuator endpoints let you monitor and interact with Syncope compo Each individual endpoint can be enabled / disabled and exposed over HTTP (pre-defined, under the `/actuator` subcontext) or JMX. -Besides a number of https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#actuator.endpoints[built-in endpoints^], +Besides a number of https://docs.spring.io/spring-boot/3.3/reference/actuator/endpoints.html[built-in endpoints^], more are made available for each component, as reported below. [NOTE] @@ -80,6 +80,6 @@ a| * `DELETE {id}` - removes the session with given `id` | `gateway` -| https://docs.spring.io/spring-cloud-gateway/docs/3.1.x/reference/html/#actuator-api[More details^] +| https://docs.spring.io/spring-cloud-gateway/docs/4.1.x/reference/html/#actuator-api[More details^] |=== diff --git a/src/main/asciidoc/reference-guide/usage/adminconsole.adoc b/src/main/asciidoc/reference-guide/usage/adminconsole.adoc index dd687009981..33f6c832a77 100644 --- a/src/main/asciidoc/reference-guide/usage/adminconsole.adoc +++ b/src/main/asciidoc/reference-guide/usage/adminconsole.adoc @@ -37,19 +37,9 @@ You can use the <> to login. The Admin UI is accessible to the visually impaired. -Two icons are present in the main login page and in the menu on the right: - -[.text-center] -image::accessibility-console01.png[title="Admin Console accessibility buttons",alt="Admin Console accessibility buttons"] - -By clicking the top right corner icon image:accessibility-icon01.png[Accessibility HC mode,30,30] it is possible to -toggle the "High contrast mode". -In this mode, the website colors are switched to a higher contrast color schema. - -[TIP] -==== The `H` https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/accesskey[accesskey^] shortcut can be used to easily toggle "High contrast mode" by using the keyboard. +In this mode, the website colors are switched to a higher contrast color schema. E.g. |=== @@ -59,16 +49,9 @@ E.g. |Toggle "High contrast mode" on Firefox and Chrome browsers on Linux |=== -==== - -By clicking the second icon image:accessibility-icon02.png[Accessibility Increased Font mode,30,30] it is possible -to toggle the "Increased font mode". -In this mode, the website font size is increased. - -[TIP] -==== The `F` https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/accesskey[accesskey^] shortcut can be used to easily toggle "Increased font mode" by using the keyboard. +In this mode, the website font size is increased. E.g. |=== @@ -78,10 +61,6 @@ E.g. |Toggle "Increased font mode" on Firefox and Chrome browsers on Linux |=== -==== - -To reset to the default mode, it is enough to click again on the specific icon. - ==== Pages [discrete] diff --git a/src/main/asciidoc/reference-guide/usage/core.adoc b/src/main/asciidoc/reference-guide/usage/core.adoc index f4e85db6870..c15a8af491e 100644 --- a/src/main/asciidoc/reference-guide/usage/core.adoc +++ b/src/main/asciidoc/reference-guide/usage/core.adoc @@ -98,7 +98,7 @@ The set of RESTful services provided by Apache Syncope can be divided as: . endpoints disclosing information about the given Syncope deployment (available <>, configured <>, Groups, ...), requiring some sort of shared authentication defined by the `anonymousKey` value in the `security.properties` file - for more information, read about Spring Security's -https://docs.spring.io/spring-security/site/docs/5.5.x/reference/html5/#anonymous[Anonymous Authentication^]; +https://docs.spring.io/spring-security/reference/6.3/servlet/authentication/anonymous.html#page-title[Anonymous Authentication^]; . endpoints for self-service (self-update, password change, ...), requiring user authentication and no entitlements; . endpoints for administrative operations, requiring user authentication with authorization granted by the related <>, handed over to users via <>. diff --git a/src/main/asciidoc/reference-guide/usage/customization.adoc b/src/main/asciidoc/reference-guide/usage/customization.adoc index cd7222be138..4a11d2e3b93 100644 --- a/src/main/asciidoc/reference-guide/usage/customization.adoc +++ b/src/main/asciidoc/reference-guide/usage/customization.adoc @@ -78,7 +78,7 @@ Do not forget to define the following system properties: [TIP] .JPDA Debug in Embedded Mode ==== -The Java™ Platform Debugger Architecture (https://docs.oracle.com/en/java/javase/11/docs/specs/jpda/jpda.html[JPDA^]) +The Java™ Platform Debugger Architecture (https://docs.oracle.com/en/java/javase/21/docs/specs/jpda/jpda.html[JPDA^]) is a collection of APIs aimed to help with debugging Java code. Enhancing the `embedded` profile of the `fit` module to enable the JPDA socket is quite @@ -155,7 +155,7 @@ endif::[] ===== Extending configuration Apache Syncope <> are built on https://spring.io/projects/spring-boot[Spring Boot^], hence designing and extending Syncope configuration very much comes down to -https://docs.spring.io/spring-boot/docs/current/reference/html/[their guide^], some aspects of which are briefly +https://docs.spring.io/spring-boot/3.3/index.html[their guide^], some aspects of which are briefly highlighted here. To design your own configuration class, take inspiration from the following sample: @@ -236,7 +236,7 @@ $ mkdir /opt/syncope/conf [TIP] ==== The `conf` directory must be configured for deployment, following Spring Boot's -https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#features.external-config.files[Externalized Configuration^] +https://docs.spring.io/spring-boot/3.3/reference/features/external-config.html[Externalized Configuration^] settings; with above reference: * <>: `--spring.config.additional-location=/opt/syncope/conf/` @@ -255,9 +255,10 @@ Besides replacing existing classes as explained <>, new be provided - in the source tree under `core/src/main/java` when Java or via REST services if Groovy - for the following components: -* <>, <>, <>, <> and <> actions -* <> / <> correlation rules +* <>, <>, <>, <> and <> actions +* <> / <> correlation rules * <> +* <> * <> * <> * <> @@ -382,7 +383,7 @@ elasticsearch.numberOfReplicas=1 as `core/src/main/resources/core-elasticsearch.properties`. Do not forget to include `elasticsearch` as -https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.profiles.adding-active-profiles[Spring Boot profile^] +https://docs.spring.io/spring-boot/3.3/reference/features/profiles.html[Spring Boot profile^] for the Core application. If needed, customize the `@Bean` declarations from @@ -435,7 +436,7 @@ opensearch.numberOfReplicas=1 as `core/src/main/resources/core-opensearch.properties`. Do not forget to include `opensearch` as -https://docs.spring.io/spring-boot/docs/2.7.x/reference/html/features.html#features.profiles.adding-active-profiles[Spring Boot profile^] +https://docs.spring.io/spring-boot/3.3/reference/features/profiles.html#features.profiles.adding-active-profiles[Spring Boot profile^] for the Core application. If needed, customize the `@Bean` declarations from @@ -480,7 +481,7 @@ Add the following dependencies to `core/pom.xml`: Adding a new REST endpoint involves several operations: . create - in an extension's `rest-api` module or under `common` otherwise - a Java interface with package -`org.apache.syncope.common.rest.api.service` and proper JAX-RS annotations; check +`org.apache.syncope.common.rest.api.service` and proper Jakarta RESTful Web Services annotations; check ifeval::["{snapshotOrRelease}" == "release"] https://github.com/apache/syncope/blob/syncope-{docVersion}/ext/flowable/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/BpmnProcessService.java[BpmnProcessService^] endif::[] diff --git a/wa/starter/pom.xml b/wa/starter/pom.xml index fffce787d33..ce86c7f91cb 100644 --- a/wa/starter/pom.xml +++ b/wa/starter/pom.xml @@ -514,11 +514,6 @@ under the License. - - org.springframework.boot - spring-boot-starter-undertow - - org.apache.syncope.common.keymaster.self syncope-common-keymaster-client-self