[SYNCOPE-470] Implementation and documentation provided
authorFrancesco Chicchiriccò <ilgrosso@apache.org>
Fri, 15 Jun 2018 14:10:45 +0000 (16:10 +0200)
committerFrancesco Chicchiriccò <ilgrosso@apache.org>
Fri, 15 Jun 2018 15:34:16 +0000 (17:34 +0200)
113 files changed:
client/console/src/main/java/org/apache/syncope/client/console/init/ClassPathScanImplementationLookup.java
client/console/src/main/java/org/apache/syncope/client/console/pages/Policies.java
client/console/src/main/java/org/apache/syncope/client/console/panels/ImplementationModalPanel.java
client/console/src/main/java/org/apache/syncope/client/console/policies/PolicyDirectoryPanel.java
client/console/src/main/java/org/apache/syncope/client/console/policies/PolicyModalPanelBuilder.java
client/console/src/main/java/org/apache/syncope/client/console/policies/ProvisioningPolicyModalPanel.java [moved from client/console/src/main/java/org/apache/syncope/client/console/policies/PullPolicyModalPanel.java with 67% similarity]
client/console/src/main/java/org/apache/syncope/client/console/policies/PullPolicyDirectoryPanel.java
client/console/src/main/java/org/apache/syncope/client/console/policies/PushPolicyDirectoryPanel.java [new file with mode: 0644]
client/console/src/main/java/org/apache/syncope/client/console/rest/ImplementationRestClient.java
client/console/src/main/java/org/apache/syncope/client/console/rest/RemediationRestClient.java
client/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPushCorrelationRule.groovy [new file with mode: 0644]
client/console/src/main/resources/org/apache/syncope/client/console/pages/Policies.properties
client/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_it.properties
client/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_ja.properties
client/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_pt_BR.properties
client/console/src/main/resources/org/apache/syncope/client/console/pages/Policies_ru.properties
client/console/src/main/resources/org/apache/syncope/client/console/policies/ProvisioningPolicyModalPanel$CorrelationRulePanel.html [moved from client/console/src/main/resources/org/apache/syncope/client/console/policies/PullPolicyModalPanel$CorrelationRulePanel.html with 93% similarity]
client/console/src/main/resources/org/apache/syncope/client/console/policies/ProvisioningPolicyModalPanel.html [moved from client/console/src/main/resources/org/apache/syncope/client/console/policies/PullPolicyModalPanel.html with 100% similarity]
client/console/src/main/resources/org/apache/syncope/client/console/policies/ProvisioningPolicyModalPanel.properties [moved from client/console/src/main/resources/org/apache/syncope/client/console/policies/PullPolicyModalPanel.properties with 94% similarity]
client/console/src/main/resources/org/apache/syncope/client/console/policies/ProvisioningPolicyModalPanel_it.properties [moved from client/console/src/main/resources/org/apache/syncope/client/console/policies/PullPolicyModalPanel_it.properties with 93% similarity]
client/console/src/main/resources/org/apache/syncope/client/console/policies/ProvisioningPolicyModalPanel_ja.properties [moved from client/console/src/main/resources/org/apache/syncope/client/console/policies/PullPolicyModalPanel_ja.properties with 94% similarity]
client/console/src/main/resources/org/apache/syncope/client/console/policies/ProvisioningPolicyModalPanel_pt_BR.properties [moved from client/console/src/main/resources/org/apache/syncope/client/console/policies/PullPolicyModalPanel_pt_BR.properties with 94% similarity]
client/console/src/main/resources/org/apache/syncope/client/console/policies/ProvisioningPolicyModalPanel_ru.properties [moved from client/console/src/main/resources/org/apache/syncope/client/console/policies/PullPolicyModalPanel_ru.properties with 95% similarity]
common/lib/src/main/java/org/apache/syncope/common/lib/LogOutputStream.java
common/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractCorrelationRuleConf.java [moved from common/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractPullCorrelationRuleConf.java with 83% similarity]
common/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultPullCorrelationRuleConf.java
common/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultPushCorrelationRuleConf.java [new file with mode: 0644]
common/lib/src/main/java/org/apache/syncope/common/lib/policy/PolicyTO.java
common/lib/src/main/java/org/apache/syncope/common/lib/policy/ProvisioningPolicyTO.java [new file with mode: 0644]
common/lib/src/main/java/org/apache/syncope/common/lib/policy/PullPolicyTO.java
common/lib/src/main/java/org/apache/syncope/common/lib/policy/PushCorrelationRuleConf.java [moved from common/lib/src/main/java/org/apache/syncope/common/lib/policy/PushPolicySpec.java with 78% similarity]
common/lib/src/main/java/org/apache/syncope/common/lib/policy/PushPolicyTO.java [new file with mode: 0644]
common/lib/src/main/java/org/apache/syncope/common/lib/to/ResourceTO.java
common/lib/src/main/java/org/apache/syncope/common/lib/types/ImplementationType.java
core/logic/src/main/java/org/apache/syncope/core/logic/ImplementationLogic.java
core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java
core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PolicyDAO.java
core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PushCorrelationRule.java [new file with mode: 0644]
core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PushCorrelationRuleConfClass.java [new file with mode: 0644]
core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AnyUtils.java
core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/CorrelationRuleEntity.java [moved from core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/CorrelationRule.java with 90% similarity]
core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/ProvisioningPolicy.java [new file with mode: 0644]
core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PullCorrelationRuleEntity.java [new file with mode: 0644]
core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PullPolicy.java
core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PushCorrelationRuleEntity.java [new file with mode: 0644]
core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PushPolicy.java
core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/resource/ExternalResource.java
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPullCorrelationRule.java
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPushCorrelationRule.java [new file with mode: 0644]
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/HaveIBeenPwnedPasswordRule.java
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAExternalResourceDAO.java
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPolicyDAO.java
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAnyUtils.java
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/AbstractCorrelationRuleEntity.java [moved from core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPACorrelationRule.java with 67% similarity]
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/AbstractProvisioningPolicy.java [new file with mode: 0644]
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPolicyUtilsFactory.java
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPullCorrelationRuleEntity.java [new file with mode: 0644]
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPullPolicy.java
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPushCorrelationRuleEntity.java [new file with mode: 0644]
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPushPolicy.java
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResource.java
core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyImplementationLookup.java
core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ImplementationTest.java
core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java
core/persistence-jpa/src/test/resources/domains/MasterContent.xml
core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/MappingManager.java
core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/ProvisioningProfile.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/MappingManagerImpl.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AbstractAnyDataBinder.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ImplementationDataBinderImpl.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/jexl/SyncopeJexlFunctions.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPullResultHandler.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractPushResultHandler.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/AbstractSyncopeResultHandler.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultAnyObjectPullResultHandler.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultAnyObjectPushResultHandler.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultGroupPullResultHandler.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultGroupPushResultHandler.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPullResultHandler.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultUserPushResultHandler.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullJobDelegate.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PullUtils.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushJobDelegate.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushUtils.java [new file with mode: 0644]
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePushJobDelegate.java
core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java
core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DummyImplementationLookup.java
core/spring/src/main/java/org/apache/syncope/core/spring/ImplementationManager.java
core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTSSOProvider.java
core/spring/src/test/java/org/apache/syncope/core/spring/security/DummyImplementationLookup.java
ext/oidcclient/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/OIDCProviderValidator.java
fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DummyPullCorrelationRule.java
fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DummyPullCorrelationRuleConf.java
fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DummyPushCorrelationRule.java [new file with mode: 0644]
fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DummyPushCorrelationRuleConf.java [new file with mode: 0644]
fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
fit/core-reference/src/test/java/org/apache/syncope/fit/core/PolicyITCase.java
fit/core-reference/src/test/java/org/apache/syncope/fit/core/PullTaskITCase.java
fit/core-reference/src/test/java/org/apache/syncope/fit/core/PushTaskITCase.java
fit/core-reference/src/test/resources/TestPullRule.groovy
fit/core-reference/src/test/resources/TestPushRule.groovy [new file with mode: 0644]
src/main/asciidoc/reference-guide/concepts/policies.adoc

index 22ac2f4..876d299 100644 (file)
@@ -41,6 +41,7 @@ import org.apache.syncope.client.console.widgets.BaseExtWidget;
 import org.apache.syncope.common.lib.policy.AccountRuleConf;
 import org.apache.syncope.common.lib.policy.PasswordRuleConf;
 import org.apache.syncope.common.lib.policy.PullCorrelationRuleConf;
+import org.apache.syncope.common.lib.policy.PushCorrelationRuleConf;
 import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.common.lib.to.GroupTO;
@@ -102,6 +103,8 @@ public class ClassPathScanImplementationLookup {
 
     private Map<String, Class<? extends PullCorrelationRuleConf>> pullCorrelationRuleConfs;
 
+    private Map<String, Class<? extends PushCorrelationRuleConf>> pushCorrelationRuleConfs;
+
     /**
      * This method can be overridden by subclasses to customize classpath scan.
      *
@@ -122,6 +125,7 @@ public class ClassPathScanImplementationLookup {
         accountRuleConfs = new HashMap<>();
         passwordRuleConfs = new HashMap<>();
         pullCorrelationRuleConfs = new HashMap<>();
+        pushCorrelationRuleConfs = new HashMap<>();
 
         ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
         scanner.addIncludeFilter(new AssignableTypeFilter(BasePage.class));
@@ -133,6 +137,7 @@ public class ClassPathScanImplementationLookup {
         scanner.addIncludeFilter(new AssignableTypeFilter(AccountRuleConf.class));
         scanner.addIncludeFilter(new AssignableTypeFilter(PasswordRuleConf.class));
         scanner.addIncludeFilter(new AssignableTypeFilter(PullCorrelationRuleConf.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(PushCorrelationRuleConf.class));
 
         scanner.findCandidateComponents(getBasePackage()).forEach(bd -> {
             try {
@@ -169,6 +174,8 @@ public class ClassPathScanImplementationLookup {
                         passwordRuleConfs.put(clazz.getName(), (Class<? extends PasswordRuleConf>) clazz);
                     } else if (PullCorrelationRuleConf.class.isAssignableFrom(clazz)) {
                         pullCorrelationRuleConfs.put(clazz.getName(), (Class<? extends PullCorrelationRuleConf>) clazz);
+                    } else if (PushCorrelationRuleConf.class.isAssignableFrom(clazz)) {
+                        pushCorrelationRuleConfs.put(clazz.getName(), (Class<? extends PushCorrelationRuleConf>) clazz);
                     }
                 }
             } catch (Throwable t) {
@@ -196,6 +203,7 @@ public class ClassPathScanImplementationLookup {
         accountRuleConfs = Collections.unmodifiableMap(accountRuleConfs);
         passwordRuleConfs = Collections.unmodifiableMap(passwordRuleConfs);
         pullCorrelationRuleConfs = Collections.unmodifiableMap(pullCorrelationRuleConfs);
+        pushCorrelationRuleConfs = Collections.unmodifiableMap(pushCorrelationRuleConfs);
 
         LOG.debug("Binary previewers found: {}", previewers);
         LOG.debug("Extension pages found: {}", extPages);
@@ -205,6 +213,7 @@ public class ClassPathScanImplementationLookup {
         LOG.debug("Account Rule configurations found: {}", accountRuleConfs);
         LOG.debug("Password Rule configurations found: {}", passwordRuleConfs);
         LOG.debug("Pull Correlation Rule configurations found: {}", pullCorrelationRuleConfs);
+        LOG.debug("Push Correlation Rule configurations found: {}", pushCorrelationRuleConfs);
     }
 
     public Class<? extends AbstractBinaryPreviewer> getPreviewerClass(final String mimeType) {
@@ -253,4 +262,7 @@ public class ClassPathScanImplementationLookup {
         return pullCorrelationRuleConfs;
     }
 
+    public Map<String, Class<? extends PushCorrelationRuleConf>> getPushCorrelationRuleConfs() {
+        return pushCorrelationRuleConfs;
+    }
 }
index 508c6c8..294b238 100644 (file)
@@ -25,6 +25,7 @@ import org.apache.syncope.client.console.BookmarkablePageLinkBuilder;
 import org.apache.syncope.client.console.policies.AccountPolicyDirectoryPanel;
 import org.apache.syncope.client.console.policies.PasswordPolicyDirectoryPanel;
 import org.apache.syncope.client.console.policies.PullPolicyDirectoryPanel;
+import org.apache.syncope.client.console.policies.PushPolicyDirectoryPanel;
 import org.apache.wicket.extensions.markup.html.tabs.AbstractTab;
 import org.apache.wicket.extensions.markup.html.tabs.ITab;
 import org.apache.wicket.markup.html.WebMarkupContainer;
@@ -81,6 +82,16 @@ public class Policies extends BasePage {
             }
         });
 
+        tabs.add(new AbstractTab(new ResourceModel("policy.push")) {
+
+            private static final long serialVersionUID = -6815067322125799251L;
+
+            @Override
+            public Panel getPanel(final String panelId) {
+                return new PushPolicyDirectoryPanel(panelId, getPageReference());
+            }
+        });
+
         return tabs;
     }
 }
index f1b3f5b..5f3fdae 100644 (file)
@@ -86,6 +86,7 @@ public class ImplementationModalPanel extends AbstractModalPanel<ImplementationT
                 || implementation.getType() == ImplementationType.ACCOUNT_RULE
                 || implementation.getType() == ImplementationType.PASSWORD_RULE
                 || implementation.getType() == ImplementationType.PULL_CORRELATION_RULE
+                || implementation.getType() == ImplementationType.PUSH_CORRELATION_RULE
                 ? ViewMode.JSON_BODY
                 : ViewMode.JAVA_CLASS;
         this.create = implementation.getKey() == null;
@@ -127,6 +128,11 @@ public class ImplementationModalPanel extends AbstractModalPanel<ImplementationT
                             collect(Collectors.toList());
                     break;
 
+                case PUSH_CORRELATION_RULE:
+                    classes = implementationLookup.getPushCorrelationRuleConfs().keySet().stream().
+                            collect(Collectors.toList());
+                    break;
+
                 default:
             }
         }
@@ -216,6 +222,10 @@ public class ImplementationModalPanel extends AbstractModalPanel<ImplementationT
                     templateClassName = "MyPullCorrelationRule";
                     break;
 
+                case PUSH_CORRELATION_RULE:
+                    templateClassName = "MyPushCorrelationRule";
+                    break;
+
                 case VALIDATOR:
                     templateClassName = "MyValidator";
                     break;
@@ -275,6 +285,10 @@ public class ImplementationModalPanel extends AbstractModalPanel<ImplementationT
                         clazz = implementationLookup.getPullCorrelationRuleConfs().get(jsonClass.getModelObject());
                         break;
 
+                    case PUSH_CORRELATION_RULE:
+                        clazz = implementationLookup.getPushCorrelationRuleConfs().get(jsonClass.getModelObject());
+                        break;
+
                     default:
                 }
 
index ab029ef..aae9114 100644 (file)
@@ -123,8 +123,10 @@ public abstract class PolicyDirectoryPanel<T extends PolicyTO>
                 new StringResourceModel("description", this), "description", "description"));
         columns.add(new CollectionPropertyColumn<>(
                 new StringResourceModel("usedByResources", this), "usedByResources"));
-        columns.add(new CollectionPropertyColumn<>(
-                new StringResourceModel("usedByRealms", this), "usedByRealms"));
+        if (type != PolicyType.PULL && type != PolicyType.PUSH) {
+            columns.add(new CollectionPropertyColumn<>(
+                    new StringResourceModel("usedByRealms", this), "usedByRealms"));
+        }
 
         addCustomColumnFields(columns);
 
index 1be9976..6bad2d4 100644 (file)
@@ -43,7 +43,7 @@ import org.apache.syncope.client.console.wizards.AjaxWizard;
 import org.apache.syncope.common.lib.policy.PolicyTO;
 import org.apache.syncope.common.lib.policy.AccountPolicyTO;
 import org.apache.syncope.common.lib.policy.PasswordPolicyTO;
-import org.apache.syncope.common.lib.policy.PullPolicyTO;
+import org.apache.syncope.common.lib.policy.ProvisioningPolicyTO;
 import org.apache.syncope.common.lib.to.EntityTO;
 import org.apache.syncope.common.lib.types.ConflictResolutionAction;
 import org.apache.syncope.common.lib.types.PolicyType;
@@ -113,7 +113,7 @@ public class PolicyModalPanelBuilder<T extends PolicyTO> extends AbstractModalPa
                         "field",
                         "maxAuthenticationAttempts",
                         Integer.class,
-                        new PropertyModel<Integer>(policyTO, "maxAuthenticationAttempts")));
+                        new PropertyModel<>(policyTO, "maxAuthenticationAttempts")));
 
                 fields.add(new AjaxCheckBoxPanel(
                         "field",
@@ -123,21 +123,21 @@ public class PolicyModalPanelBuilder<T extends PolicyTO> extends AbstractModalPa
 
                 fields.add(new AjaxPalettePanel.Builder<String>().setName("passthroughResources").build(
                         "field",
-                        new PropertyModel<List<String>>(policyTO, "passthroughResources"),
-                        new ListModel<String>(resources.getObject())));
+                        new PropertyModel<>(policyTO, "passthroughResources"),
+                        new ListModel<>(resources.getObject())));
             } else if (policyTO instanceof PasswordPolicyTO) {
                 fields.add(new AjaxSpinnerFieldPanel.Builder<Integer>().build(
                         "field",
                         "historyLength",
                         Integer.class,
-                        new PropertyModel<Integer>(policyTO, "historyLength")));
+                        new PropertyModel<>(policyTO, "historyLength")));
 
                 fields.add(new AjaxCheckBoxPanel(
                         "field",
                         "allowNullPassword",
                         new PropertyModel<>(policyTO, "allowNullPassword"),
                         false));
-            } else if (policyTO instanceof PullPolicyTO) {
+            } else if (policyTO instanceof ProvisioningPolicyTO) {
                 fields.add(new AjaxDropDownChoicePanel<>(
                         "field",
                         "conflictResolutionAction",
@@ -37,10 +37,14 @@ import org.apache.syncope.client.console.rest.PolicyRestClient;
 import org.apache.syncope.client.console.rest.SchemaRestClient;
 import org.apache.syncope.client.console.wicket.ajax.form.IndicatorAjaxFormComponentUpdatingBehavior;
 import org.apache.syncope.client.console.wicket.markup.html.bootstrap.dialog.BaseModal;
+import org.apache.syncope.client.console.wicket.markup.html.form.AjaxCheckBoxPanel;
 import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
 import org.apache.syncope.client.console.wicket.markup.html.form.AjaxPalettePanel;
 import org.apache.syncope.client.console.wicket.markup.html.form.MultiPanel;
+import org.apache.syncope.common.lib.policy.AbstractCorrelationRuleConf;
 import org.apache.syncope.common.lib.policy.DefaultPullCorrelationRuleConf;
+import org.apache.syncope.common.lib.policy.DefaultPushCorrelationRuleConf;
+import org.apache.syncope.common.lib.policy.ProvisioningPolicyTO;
 import org.apache.syncope.common.lib.policy.PullPolicyTO;
 import org.apache.syncope.common.lib.to.EntityTO;
 import org.apache.syncope.common.lib.to.ImplementationTO;
@@ -58,7 +62,7 @@ import org.apache.wicket.model.LoadableDetachableModel;
 import org.apache.wicket.model.Model;
 import org.apache.wicket.model.PropertyModel;
 
-public class PullPolicyModalPanel extends AbstractModalPanel<PullPolicyTO> {
+public class ProvisioningPolicyModalPanel extends AbstractModalPanel<ProvisioningPolicyTO> {
 
     private static final long serialVersionUID = 2988891313881271124L;
 
@@ -70,34 +74,42 @@ public class PullPolicyModalPanel extends AbstractModalPanel<PullPolicyTO> {
 
     private final SchemaRestClient schemaRestClient = new SchemaRestClient();
 
-    private final LoadableDetachableModel<Map<String, ImplementationTO>> implementations =
-            new LoadableDetachableModel<Map<String, ImplementationTO>>() {
-
-        private static final long serialVersionUID = 5275935387613157437L;
-
-        @Override
-        protected Map<String, ImplementationTO> load() {
-            return implRestClient.list(ImplementationType.PULL_CORRELATION_RULE).stream().
-                    collect(Collectors.toMap(EntityTO::getKey, Function.identity()));
-        }
-    };
+    private final LoadableDetachableModel<Map<String, ImplementationTO>> implementations;
 
     private final IModel<List<CorrelationRule>> model;
 
-    public PullPolicyModalPanel(
-            final PullPolicyTO policyTO,
-            final BaseModal<PullPolicyTO> modal,
+    @SuppressWarnings("unchecked")
+    public ProvisioningPolicyModalPanel(
+            final ProvisioningPolicyTO policyTO,
+            final BaseModal<? extends ProvisioningPolicyTO> modal,
             final PageReference pageRef) {
 
-        super(modal, pageRef);
-        modal.setFormModel(policyTO);
+        super((BaseModal<ProvisioningPolicyTO>) modal, pageRef);
+        ((BaseModal<ProvisioningPolicyTO>) modal).setFormModel(policyTO);
+
+        implementations = new LoadableDetachableModel<Map<String, ImplementationTO>>() {
+
+            private static final long serialVersionUID = 5275935387613157437L;
+
+            @Override
+            protected Map<String, ImplementationTO> load() {
+                return implRestClient.list(policyTO instanceof PullPolicyTO
+                        ? ImplementationType.PULL_CORRELATION_RULE
+                        : ImplementationType.PUSH_CORRELATION_RULE).stream().
+                        collect(Collectors.toMap(EntityTO::getKey, Function.identity()));
+            }
+        };
 
         model = new PropertyModel<List<CorrelationRule>>(policyTO, "correlationRules") {
 
             private static final long serialVersionUID = -8168676563540297301L;
 
             private final List<CorrelationRule> rules = policyTO.getCorrelationRules().keySet().stream().
-                    map(anyType -> new CorrelationRule(anyType,
+                    map(anyType -> new CorrelationRule(
+                    policyTO instanceof PullPolicyTO
+                            ? DefaultPullCorrelationRuleConf.class
+                            : DefaultPushCorrelationRuleConf.class,
+                    anyType,
                     implementations.getObject().get(policyTO.getCorrelationRules().get(anyType)))).
                     collect(Collectors.toList());
 
@@ -121,7 +133,9 @@ public class PullPolicyModalPanel extends AbstractModalPanel<PullPolicyTO> {
 
             @Override
             protected CorrelationRule newModelObject() {
-                return new CorrelationRule();
+                return new CorrelationRule(policyTO instanceof PullPolicyTO
+                        ? DefaultPullCorrelationRuleConf.class
+                        : DefaultPushCorrelationRuleConf.class);
             }
 
             @Override
@@ -146,7 +160,7 @@ public class PullPolicyModalPanel extends AbstractModalPanel<PullPolicyTO> {
                     }
                 }
             });
-            restClient.updatePolicy(PolicyType.PULL, getItem());
+            restClient.updatePolicy(getItem() instanceof PullPolicyTO ? PolicyType.PULL : PolicyType.PUSH, getItem());
 
             SyncopeConsoleSession.get().info(getString(Constants.OPERATION_SUCCEEDED));
             this.modal.close(target);
@@ -182,16 +196,63 @@ public class PullPolicyModalPanel extends AbstractModalPanel<PullPolicyTO> {
             rule.setOutputMarkupId(true);
             add(rule);
 
+            PropertyModel<Boolean> orSchemasModel =
+                    new PropertyModel<Boolean>(correlationRule.getObject().getDefaultRuleConf(), "orSchemas") {
+
+                private static final long serialVersionUID = 807008909842554829L;
+
+                private boolean orSchemas() {
+                    AbstractCorrelationRuleConf conf = correlationRule.getObject().getDefaultRuleConf();
+                    return conf instanceof DefaultPullCorrelationRuleConf
+                            ? DefaultPullCorrelationRuleConf.class.cast(conf).isOrSchemas()
+                            : conf instanceof DefaultPushCorrelationRuleConf
+                                    ? DefaultPushCorrelationRuleConf.class.cast(conf).isOrSchemas()
+                                    : false;
+                }
+
+                @Override
+                public Boolean getObject() {
+                    AbstractCorrelationRuleConf conf = correlationRule.getObject().getDefaultRuleConf();
+                    return conf instanceof DefaultPullCorrelationRuleConf
+                            ? DefaultPullCorrelationRuleConf.class.cast(conf).isOrSchemas()
+                            : conf instanceof DefaultPushCorrelationRuleConf
+                                    ? DefaultPushCorrelationRuleConf.class.cast(conf).isOrSchemas()
+                                    : false;
+                }
+
+                @Override
+                public void setObject(final Boolean object) {
+                    AbstractCorrelationRuleConf conf = correlationRule.getObject().getDefaultRuleConf();
+                    if (conf instanceof DefaultPullCorrelationRuleConf) {
+                        DefaultPullCorrelationRuleConf.class.cast(conf).setOrSchemas(object);
+                    } else if (conf instanceof DefaultPushCorrelationRuleConf) {
+                        DefaultPushCorrelationRuleConf.class.cast(conf).setOrSchemas(object);
+                    }
+                }
+            };
+            AjaxCheckBoxPanel orSchemas = new AjaxCheckBoxPanel("orSchemas", "orSchemas", orSchemasModel, false);
+            orSchemas.setOutputMarkupPlaceholderTag(true);
+            add(orSchemas.setVisible(correlationRule.getObject().getDefaultRuleConf() != null));
+
             PropertyModel<List<String>> defaultRuleConfModel =
                     new PropertyModel<List<String>>(correlationRule.getObject().getDefaultRuleConf(), "schemas") {
 
                 private static final long serialVersionUID = 3799387950428254072L;
 
+                private List<String> schemas() {
+                    AbstractCorrelationRuleConf conf = correlationRule.getObject().getDefaultRuleConf();
+                    return conf instanceof DefaultPullCorrelationRuleConf
+                            ? DefaultPullCorrelationRuleConf.class.cast(conf).getSchemas()
+                            : conf instanceof DefaultPushCorrelationRuleConf
+                                    ? DefaultPushCorrelationRuleConf.class.cast(conf).getSchemas()
+                                    : Collections.emptyList();
+                }
+
                 @Override
                 public List<String> getObject() {
                     List<String> schemas = new ArrayList<>();
                     if (correlationRule.getObject().getDefaultRuleConf() != null) {
-                        schemas.addAll(correlationRule.getObject().getDefaultRuleConf().getSchemas());
+                        schemas.addAll(schemas());
                     }
                     return schemas;
                 }
@@ -199,12 +260,11 @@ public class PullPolicyModalPanel extends AbstractModalPanel<PullPolicyTO> {
                 @Override
                 public void setObject(final List<String> object) {
                     if (correlationRule.getObject().getDefaultRuleConf() != null) {
-                        correlationRule.getObject().getDefaultRuleConf().getSchemas().clear();
-                        correlationRule.getObject().getDefaultRuleConf().getSchemas().addAll(object);
+                        schemas().clear();
+                        schemas().addAll(object);
                     }
                 }
             };
-
             AjaxPalettePanel<String> defaultRuleConf = new AjaxPalettePanel.Builder<String>().
                     setName("defaultRuleConf").build("defaultRuleConf",
                     defaultRuleConfModel, new AjaxPalettePanel.Builder.Query<String>() {
@@ -225,6 +285,9 @@ public class PullPolicyModalPanel extends AbstractModalPanel<PullPolicyTO> {
 
                 @Override
                 protected void onUpdate(final AjaxRequestTarget target) {
+                    if (orSchemas.isVisibleInHierarchy()) {
+                        target.add(orSchemas);
+                    }
                     if (defaultRuleConf.isVisibleInHierarchy()) {
                         correlationRule.getObject().setImpl(null);
                         defaultRuleConf.reload(target);
@@ -240,10 +303,13 @@ public class PullPolicyModalPanel extends AbstractModalPanel<PullPolicyTO> {
                 @Override
                 protected void onUpdate(final AjaxRequestTarget target) {
                     if (correlationRule.getObject().getDefaultRuleConf() == null) {
+                        orSchemas.setVisible(false);
                         defaultRuleConf.setVisible(false);
                     } else {
+                        orSchemas.setVisible(true);
                         defaultRuleConf.setVisible(true);
                     }
+                    target.add(orSchemas);
                     target.add(defaultRuleConf);
                 }
             });
@@ -270,17 +336,25 @@ public class PullPolicyModalPanel extends AbstractModalPanel<PullPolicyTO> {
 
         private static final long serialVersionUID = 4221521483948294336L;
 
+        private final Class<? extends AbstractCorrelationRuleConf> ruleConfClass;
+
         private String anyType;
 
         private ImplementationTO impl;
 
-        private DefaultPullCorrelationRuleConf defaultRuleConf;
+        private AbstractCorrelationRuleConf defaultRuleConf;
 
-        CorrelationRule() {
+        CorrelationRule(final Class<? extends AbstractCorrelationRuleConf> ruleConfClass) {
+            this.ruleConfClass = ruleConfClass;
             this.anyType = AnyTypeKind.USER.name();
         }
 
-        CorrelationRule(final String anyType, final ImplementationTO impl) {
+        CorrelationRule(
+                final Class<? extends AbstractCorrelationRuleConf> ruleConfClass,
+                final String anyType,
+                final ImplementationTO impl) {
+
+            this.ruleConfClass = ruleConfClass;
             this.anyType = anyType;
             setImpl(impl);
         }
@@ -306,11 +380,10 @@ public class PullPolicyModalPanel extends AbstractModalPanel<PullPolicyTO> {
             if (impl != null) {
                 this.defaultRuleConf = null;
                 try {
-                    this.defaultRuleConf = OBJECT_MAPPER.readValue(
-                            impl.getBody(), DefaultPullCorrelationRuleConf.class);
+                    this.defaultRuleConf = OBJECT_MAPPER.readValue(impl.getBody(), ruleConfClass);
                 } catch (Exception e) {
                     LOG.debug("Could not deserialize {} as {}",
-                            impl.getBody(), DefaultPullCorrelationRuleConf.class.getName());
+                            impl.getBody(), ruleConfClass.getName());
                 }
             }
         }
@@ -326,13 +399,12 @@ public class PullPolicyModalPanel extends AbstractModalPanel<PullPolicyTO> {
             return impl;
         }
 
-        public void setDefaultRuleConf(final DefaultPullCorrelationRuleConf defaultRuleConf) {
+        public void setDefaultRuleConf(final DefaultPushCorrelationRuleConf defaultRuleConf) {
             this.defaultRuleConf = defaultRuleConf;
         }
 
-        public DefaultPullCorrelationRuleConf getDefaultRuleConf() {
+        public AbstractCorrelationRuleConf getDefaultRuleConf() {
             return defaultRuleConf;
         }
-
     }
 }
index e0c49a4..7861891 100644 (file)
@@ -67,7 +67,7 @@ public class PullPolicyDirectoryPanel extends PolicyDirectoryPanel<PullPolicyTO>
             @Override
             public void onClick(final AjaxRequestTarget target, final PullPolicyTO ignore) {
                 target.add(policySpecModal.setContent(
-                        new PullPolicyModalPanel(model.getObject(), policySpecModal, pageRef)));
+                        new ProvisioningPolicyModalPanel(model.getObject(), policySpecModal, pageRef)));
 
                 policySpecModal.header(new StringResourceModel(
                         "policy.rules", PullPolicyDirectoryPanel.this, Model.of(model.getObject())));
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/policies/PushPolicyDirectoryPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/policies/PushPolicyDirectoryPanel.java
new file mode 100644 (file)
index 0000000..fa50014
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * 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.policies;
+
+import java.util.List;
+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.PushPolicyTO;
+import org.apache.syncope.common.lib.types.PolicyType;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.wicket.PageReference;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.authroles.authorization.strategies.role.metadata.MetaDataRoleAuthorizationStrategy;
+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;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.model.StringResourceModel;
+
+/**
+ * Push policies page.
+ */
+public class PushPolicyDirectoryPanel extends PolicyDirectoryPanel<PushPolicyTO> {
+
+    private static final long serialVersionUID = 4984337552918213290L;
+
+    public PushPolicyDirectoryPanel(final String id, final PageReference pageRef) {
+        super(id, PolicyType.PUSH, pageRef);
+
+        final PushPolicyTO defaultItem = new PushPolicyTO();
+
+        this.addNewItemPanelBuilder(
+                new PolicyModalPanelBuilder<>(PolicyType.PUSH, defaultItem, modal, pageRef), true);
+        MetaDataRoleAuthorizationStrategy.authorize(addAjaxLink, RENDER, StandardEntitlement.POLICY_CREATE);
+
+        initResultTable();
+    }
+
+    @Override
+    protected void addCustomColumnFields(final List<IColumn<PushPolicyTO, String>> columns) {
+        columns.add(new PropertyColumn<>(new StringResourceModel(
+                "conflictResolutionAction", this), "conflictResolutionAction", "conflictResolutionAction"));
+    }
+
+    @Override
+    protected void addCustomActions(final ActionsPanel<PushPolicyTO> panel, final IModel<PushPolicyTO> model) {
+        panel.add(new ActionLink<PushPolicyTO>() {
+
+            private static final long serialVersionUID = -3722207913631435501L;
+
+            @Override
+            public void onClick(final AjaxRequestTarget target, final PushPolicyTO ignore) {
+                target.add(policySpecModal.setContent(
+                        new ProvisioningPolicyModalPanel(model.getObject(), policySpecModal, pageRef)));
+
+                policySpecModal.header(new StringResourceModel(
+                        "policy.rules", PushPolicyDirectoryPanel.this, Model.of(model.getObject())));
+
+                MetaDataRoleAuthorizationStrategy.authorize(
+                        policySpecModal.getForm(), ENABLE, StandardEntitlement.POLICY_UPDATE);
+
+                policySpecModal.show(true);
+            }
+        }, ActionLink.ActionType.COMPOSE, StandardEntitlement.POLICY_UPDATE);
+    }
+}
index 7d99c7c..746ea70 100644 (file)
@@ -19,7 +19,6 @@
 package org.apache.syncope.client.console.rest;
 
 import java.util.List;
-import javax.ws.rs.core.Response;
 import org.apache.syncope.common.lib.to.ImplementationTO;
 import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.common.rest.api.service.ImplementationService;
@@ -36,10 +35,8 @@ public class ImplementationRestClient extends BaseRestClient {
         return getService(ImplementationService.class).read(type, key);
     }
 
-    public ImplementationTO create(final ImplementationTO implementation) {
-        ImplementationService service = getService(ImplementationService.class);
-        Response response = service.create(implementation);
-        return getObject(service, response.getLocation(), ImplementationTO.class);
+    public void create(final ImplementationTO implementation) {
+        getService(ImplementationService.class).create(implementation);
     }
 
     public void update(final ImplementationTO implementation) {
@@ -49,5 +46,4 @@ public class ImplementationRestClient extends BaseRestClient {
     public void delete(final ImplementationType type, final String key) {
         getService(ImplementationService.class).delete(type, key);
     }
-
 }
index 4dd9ca6..a5023e5 100644 (file)
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.client.console.rest;
 
-import static org.apache.syncope.client.console.rest.BaseRestClient.getService;
-
 import java.util.List;
 import javax.ws.rs.core.GenericType;
 import javax.ws.rs.core.Response;
diff --git a/client/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPushCorrelationRule.groovy b/client/console/src/main/resources/org/apache/syncope/client/console/implementations/MyPushCorrelationRule.groovy
new file mode 100644 (file)
index 0000000..ad3951d
--- /dev/null
@@ -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.core.persistence.api.dao.PushCorrelationRule
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.resource.Provision;
+import org.identityconnectors.framework.common.objects.filter.Filter;
+
+@CompileStatic
+class MyPushCorrelationRule implements PushCorrelationRule {
+
+  @Override
+  Filter getFilter(Any<?> any, Provision provision) {
+    
+  }
+}
index 20b411c..5c1a92a 100644 (file)
@@ -18,3 +18,4 @@
 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.push=Push
index 3d5069a..213d170 100644 (file)
@@ -25,7 +25,7 @@ import org.slf4j.Logger;
 
 /**
  * Delegates output stream writing onto an SLF4J logger.
- * Inspired by {@code}org.apache.commons.exec.LogOutputStream{@code}
+ * Inspired by {@code org.apache.commons.exec.LogOutputStream}
  */
 public class LogOutputStream extends OutputStream {
 
@@ -24,19 +24,19 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.AbstractBaseBean;
 
 @XmlType
-@XmlSeeAlso({ DefaultPullCorrelationRuleConf.class })
-public abstract class AbstractPullCorrelationRuleConf extends AbstractBaseBean implements PullCorrelationRuleConf {
+@XmlSeeAlso({ DefaultPullCorrelationRuleConf.class, DefaultPushCorrelationRuleConf.class })
+public abstract class AbstractCorrelationRuleConf extends AbstractBaseBean implements RuleConf {
 
     private static final long serialVersionUID = -4080475005967851092L;
 
     private String name;
 
-    public AbstractPullCorrelationRuleConf() {
+    public AbstractCorrelationRuleConf() {
         this(StringUtils.EMPTY);
         setName(getClass().getName());
     }
 
-    public AbstractPullCorrelationRuleConf(final String name) {
+    public AbstractCorrelationRuleConf(final String name) {
         super();
         this.name = name;
     }
index 151683c..9bda1a6 100644 (file)
@@ -28,17 +28,26 @@ import javax.xml.bind.annotation.XmlType;
 
 @XmlRootElement(name = "defaultPullCorrelationRuleConf")
 @XmlType
-public class DefaultPullCorrelationRuleConf extends AbstractPullCorrelationRuleConf implements PullCorrelationRuleConf {
+public class DefaultPullCorrelationRuleConf extends AbstractCorrelationRuleConf implements PullCorrelationRuleConf {
 
     private static final long serialVersionUID = 429126085793346273L;
 
+    private boolean orSchemas;
+
     private final List<String> schemas = new ArrayList<>();
 
+    public boolean isOrSchemas() {
+        return orSchemas;
+    }
+
+    public void setOrSchemas(final boolean orSchemas) {
+        this.orSchemas = orSchemas;
+    }
+
     @XmlElementWrapper(name = "schemas")
     @XmlElement(name = "schema")
     @JsonProperty("schemas")
     public List<String> getSchemas() {
         return schemas;
     }
-
 }
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultPushCorrelationRuleConf.java b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultPushCorrelationRuleConf.java
new file mode 100644 (file)
index 0000000..a909208
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * 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.policy;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+@XmlRootElement(name = "defaultPushCorrelationRuleConf")
+@XmlType
+public class DefaultPushCorrelationRuleConf extends AbstractCorrelationRuleConf implements PushCorrelationRuleConf {
+
+    private static final long serialVersionUID = 429126085793346273L;
+
+    private boolean orSchemas;
+
+    private final List<String> schemas = new ArrayList<>();
+
+    public boolean isOrSchemas() {
+        return orSchemas;
+    }
+
+    public void setOrSchemas(final boolean orSchemas) {
+        this.orSchemas = orSchemas;
+    }
+
+    @XmlElementWrapper(name = "schemas")
+    @XmlElement(name = "schema")
+    @JsonProperty("schemas")
+    public List<String> getSchemas() {
+        return schemas;
+    }
+}
index eb84fcb..98b79d6 100644 (file)
@@ -36,7 +36,7 @@ import org.apache.syncope.common.lib.to.EntityTO;
 
 @XmlRootElement(name = "policy")
 @XmlType
-@XmlSeeAlso({ AccountPolicyTO.class, PasswordPolicyTO.class, PullPolicyTO.class })
+@XmlSeeAlso({ AccountPolicyTO.class, PasswordPolicyTO.class, ProvisioningPolicyTO.class })
 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "@class")
 @JsonPropertyOrder(value = { "@class", "key", "description" })
 @Schema(
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/policy/ProvisioningPolicyTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/ProvisioningPolicyTO.java
new file mode 100644 (file)
index 0000000..e686223
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * 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.policy;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.util.HashMap;
+import java.util.Map;
+import javax.xml.bind.annotation.XmlSeeAlso;
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+import org.apache.syncope.common.lib.jaxb.XmlGenericMapAdapter;
+import org.apache.syncope.common.lib.types.ConflictResolutionAction;
+
+@XmlType
+@XmlSeeAlso({ PullPolicyTO.class, PushPolicyTO.class })
+@Schema(allOf = { PolicyTO.class })
+public abstract class ProvisioningPolicyTO extends PolicyTO {
+
+    private static final long serialVersionUID = -3786048942148269602L;
+
+    private ConflictResolutionAction conflictResolutionAction;
+
+    @XmlJavaTypeAdapter(XmlGenericMapAdapter.class)
+    private final Map<String, String> correlationRules = new HashMap<>();
+
+    public ConflictResolutionAction getConflictResolutionAction() {
+        return conflictResolutionAction == null
+                ? ConflictResolutionAction.IGNORE
+                : conflictResolutionAction;
+    }
+
+    public void setConflictResolutionAction(final ConflictResolutionAction conflictResolutionAction) {
+        this.conflictResolutionAction = conflictResolutionAction;
+    }
+
+    @JsonProperty
+    public Map<String, String> getCorrelationRules() {
+        return correlationRules;
+    }
+}
index 967f04d..aee16cd 100644 (file)
@@ -20,24 +20,17 @@ package org.apache.syncope.common.lib.policy;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.swagger.v3.oas.annotations.media.Schema;
-import java.util.HashMap;
-import java.util.Map;
 import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.XmlTransient;
 import javax.xml.bind.annotation.XmlType;
-import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
-import org.apache.syncope.common.lib.jaxb.XmlGenericMapAdapter;
-import org.apache.syncope.common.lib.types.ConflictResolutionAction;
 
 @XmlRootElement(name = "pullPolicy")
 @XmlType
-@Schema(allOf = { PolicyTO.class })
-public class PullPolicyTO extends PolicyTO {
+@Schema(allOf = { ProvisioningPolicyTO.class, PolicyTO.class })
+public class PullPolicyTO extends ProvisioningPolicyTO {
 
     private static final long serialVersionUID = 993024634238024242L;
 
-    private ConflictResolutionAction conflictResolutionAction;
-
     @XmlTransient
     @JsonProperty("@class")
     @Schema(name = "@class", required = true, example = "org.apache.syncope.common.lib.policy.PullPolicyTO")
@@ -45,22 +38,4 @@ public class PullPolicyTO extends PolicyTO {
     public String getDiscriminator() {
         return getClass().getName();
     }
-
-    @XmlJavaTypeAdapter(XmlGenericMapAdapter.class)
-    private final Map<String, String> correlationRules = new HashMap<>();
-
-    public ConflictResolutionAction getConflictResolutionAction() {
-        return conflictResolutionAction == null
-                ? ConflictResolutionAction.IGNORE
-                : conflictResolutionAction;
-    }
-
-    public void setConflictResolutionAction(final ConflictResolutionAction conflictResolutionAction) {
-        this.conflictResolutionAction = conflictResolutionAction;
-    }
-
-    @JsonProperty
-    public Map<String, String> getCorrelationRules() {
-        return correlationRules;
-    }
 }
  */
 package org.apache.syncope.common.lib.policy;
 
-import javax.xml.bind.annotation.XmlType;
-import org.apache.syncope.common.lib.AbstractBaseBean;
-
-@XmlType
-public class PushPolicySpec extends AbstractBaseBean {
-
-    private static final long serialVersionUID = 3641030189482617497L;
+public interface PushCorrelationRuleConf extends RuleConf {
 
 }
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/policy/PushPolicyTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/PushPolicyTO.java
new file mode 100644 (file)
index 0000000..a2eca14
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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.policy;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlTransient;
+import javax.xml.bind.annotation.XmlType;
+
+@XmlRootElement(name = "pushPolicy")
+@XmlType
+@Schema(allOf = { ProvisioningPolicyTO.class, PolicyTO.class })
+public class PushPolicyTO extends ProvisioningPolicyTO {
+
+    private static final long serialVersionUID = 993024634238024242L;
+
+    @XmlTransient
+    @JsonProperty("@class")
+    @Schema(name = "@class", required = true, example = "org.apache.syncope.common.lib.policy.PushPolicyTO")
+    @Override
+    public String getDiscriminator() {
+        return getClass().getName();
+    }
+}
index ab7e0e6..d0061bb 100644 (file)
@@ -77,6 +77,8 @@ public class ResourceTO extends AbstractBaseBean implements EntityTO {
 
     private String pullPolicy;
 
+    private String pushPolicy;
+
     private final List<ConnConfProperty> confOverride = new ArrayList<>();
 
     private boolean overrideCapabilities = false;
@@ -184,6 +186,14 @@ public class ResourceTO extends AbstractBaseBean implements EntityTO {
         this.pullPolicy = pullPolicy;
     }
 
+    public String getPushPolicy() {
+        return pushPolicy;
+    }
+
+    public void setPushPolicy(final String pushPolicy) {
+        this.pushPolicy = pushPolicy;
+    }
+
     @JsonIgnore
     public Optional<ProvisionTO> getProvision(final String anyType) {
         return provisions.stream().filter(
index c528274..98e10b7 100644 (file)
@@ -35,6 +35,7 @@ public enum ImplementationType {
     PULL_ACTIONS,
     PUSH_ACTIONS,
     PULL_CORRELATION_RULE,
+    PUSH_CORRELATION_RULE,
     VALIDATOR,
     RECIPIENTS_PROVIDER,
     AUDIT_APPENDER;
index 3079483..19efd1f 100644 (file)
@@ -189,7 +189,11 @@ public class ImplementationLogic extends AbstractTransactionalLogic<Implementati
                 break;
 
             case PULL_CORRELATION_RULE:
-                inUse = !policyDAO.findByCorrelationRule(implementation).isEmpty();
+                inUse = !policyDAO.findByPullCorrelationRule(implementation).isEmpty();
+                break;
+
+            case PUSH_CORRELATION_RULE:
+                inUse = !policyDAO.findByPushCorrelationRule(implementation).isEmpty();
                 break;
 
             case VALIDATOR:
index 3b824c5..6608f39 100644 (file)
@@ -28,6 +28,7 @@ import java.util.Set;
 import org.apache.syncope.common.lib.policy.AccountRuleConf;
 import org.apache.syncope.common.lib.policy.PasswordRuleConf;
 import org.apache.syncope.common.lib.policy.PullCorrelationRuleConf;
+import org.apache.syncope.common.lib.policy.PushCorrelationRuleConf;
 import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.core.logic.audit.AuditAppender;
@@ -47,6 +48,8 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
 import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
 import org.apache.syncope.core.persistence.api.dao.PullCorrelationRule;
 import org.apache.syncope.core.persistence.api.dao.PullCorrelationRuleConfClass;
+import org.apache.syncope.core.persistence.api.dao.PushCorrelationRule;
+import org.apache.syncope.core.persistence.api.dao.PushCorrelationRuleConfClass;
 import org.apache.syncope.core.provisioning.api.pushpull.PushActions;
 import org.apache.syncope.core.provisioning.api.pushpull.ReconFilterBuilder;
 import org.apache.syncope.core.provisioning.java.data.JEXLItemTransformerImpl;
@@ -79,7 +82,9 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
 
     private Map<Class<? extends PasswordRuleConf>, Class<? extends PasswordRule>> passwordRuleClasses;
 
-    private Map<Class<? extends PullCorrelationRuleConf>, Class<? extends PullCorrelationRule>> correlationRuleClasses;
+    private Map<Class<? extends PullCorrelationRuleConf>, Class<? extends PullCorrelationRule>> pullCRClasses;
+
+    private Map<Class<? extends PushCorrelationRuleConf>, Class<? extends PushCorrelationRule>> pushCRClasses;
 
     private Set<Class<?>> auditAppenderClasses;
 
@@ -109,7 +114,8 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
         reportletClasses = new HashMap<>();
         accountRuleClasses = new HashMap<>();
         passwordRuleClasses = new HashMap<>();
-        correlationRuleClasses = new HashMap<>();
+        pullCRClasses = new HashMap<>();
+        pushCRClasses = new HashMap<>();
         auditAppenderClasses = new HashSet<>();
 
         ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
@@ -118,6 +124,7 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
         scanner.addIncludeFilter(new AssignableTypeFilter(AccountRule.class));
         scanner.addIncludeFilter(new AssignableTypeFilter(PasswordRule.class));
         scanner.addIncludeFilter(new AssignableTypeFilter(PullCorrelationRule.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(PushCorrelationRule.class));
         scanner.addIncludeFilter(new AssignableTypeFilter(ItemTransformer.class));
         scanner.addIncludeFilter(new AssignableTypeFilter(SchedTaskJobDelegate.class));
         scanner.addIncludeFilter(new AssignableTypeFilter(ReconFilterBuilder.class));
@@ -176,7 +183,17 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
                         LOG.warn("Found pull correlation rule {} without declared configuration", clazz.getName());
                     } else {
                         classNames.get(ImplementationType.ACCOUNT_RULE).add(clazz.getName());
-                        correlationRuleClasses.put(annotation.value(), (Class<? extends PullCorrelationRule>) clazz);
+                        pullCRClasses.put(annotation.value(), (Class<? extends PullCorrelationRule>) clazz);
+                    }
+                }
+
+                if (PushCorrelationRule.class.isAssignableFrom(clazz) && !isAbstractClazz) {
+                    PushCorrelationRuleConfClass annotation = clazz.getAnnotation(PushCorrelationRuleConfClass.class);
+                    if (annotation == null) {
+                        LOG.warn("Found push correlation rule {} without declared configuration", clazz.getName());
+                    } else {
+                        classNames.get(ImplementationType.ACCOUNT_RULE).add(clazz.getName());
+                        pushCRClasses.put(annotation.value(), (Class<? extends PushCorrelationRule>) clazz);
                     }
                 }
 
@@ -238,7 +255,8 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
         reportletClasses = Collections.unmodifiableMap(reportletClasses);
         accountRuleClasses = Collections.unmodifiableMap(accountRuleClasses);
         passwordRuleClasses = Collections.unmodifiableMap(passwordRuleClasses);
-        correlationRuleClasses = Collections.unmodifiableMap(correlationRuleClasses);
+        pullCRClasses = Collections.unmodifiableMap(pullCRClasses);
+        pushCRClasses = Collections.unmodifiableMap(pushCRClasses);
         auditAppenderClasses = Collections.unmodifiableSet(auditAppenderClasses);
     }
 
@@ -277,7 +295,14 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
     public Class<? extends PullCorrelationRule> getPullCorrelationRuleClass(
             final Class<? extends PullCorrelationRuleConf> correlationRuleConfClass) {
 
-        return correlationRuleClasses.get(correlationRuleConfClass);
+        return pullCRClasses.get(correlationRuleConfClass);
+    }
+
+    @Override
+    public Class<? extends PushCorrelationRule> getPushCorrelationRuleClass(
+            final Class<? extends PushCorrelationRuleConf> correlationRuleConfClass) {
+
+        return pushCRClasses.get(correlationRuleConfClass);
     }
 
     @Override
index fce4be4..4488568 100644 (file)
@@ -22,11 +22,13 @@ import java.util.Set;
 import org.apache.syncope.common.lib.policy.AccountRuleConf;
 import org.apache.syncope.common.lib.policy.PasswordRuleConf;
 import org.apache.syncope.common.lib.policy.PullCorrelationRuleConf;
+import org.apache.syncope.common.lib.policy.PushCorrelationRuleConf;
 import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.core.persistence.api.dao.AccountRule;
 import org.apache.syncope.core.persistence.api.dao.PasswordRule;
 import org.apache.syncope.core.persistence.api.dao.PullCorrelationRule;
+import org.apache.syncope.core.persistence.api.dao.PushCorrelationRule;
 import org.apache.syncope.core.persistence.api.dao.Reportlet;
 
 public interface ImplementationLookup extends SyncopeLoader {
@@ -47,5 +49,8 @@ public interface ImplementationLookup extends SyncopeLoader {
     Class<? extends PullCorrelationRule> getPullCorrelationRuleClass(
             Class<? extends PullCorrelationRuleConf> pullCorrelationRuleConfClass);
 
+    Class<? extends PushCorrelationRule> getPushCorrelationRuleClass(
+            Class<? extends PushCorrelationRuleConf> pushCorrelationRuleConfClass);
+
     Set<Class<?>> getAuditAppenderClasses();
 }
index e841a3c..6765030 100644 (file)
@@ -24,6 +24,7 @@ import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 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;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 
 public interface PolicyDAO extends DAO<Policy> {
@@ -36,7 +37,9 @@ public interface PolicyDAO extends DAO<Policy> {
 
     List<PasswordPolicy> findByPasswordRule(Implementation passwordRule);
 
-    List<PullPolicy> findByCorrelationRule(Implementation correlationRule);
+    List<PullPolicy> findByPullCorrelationRule(Implementation correlationRule);
+
+    List<PushPolicy> findByPushCorrelationRule(Implementation correlationRule);
 
     List<AccountPolicy> findByResource(ExternalResource resource);
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PushCorrelationRule.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PushCorrelationRule.java
new file mode 100644 (file)
index 0000000..0b042a5
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * 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.dao;
+
+import org.apache.syncope.common.lib.policy.PushCorrelationRuleConf;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.resource.Provision;
+import org.identityconnectors.framework.common.objects.filter.Filter;
+
+/**
+ * Interface for correlation rule to be evaluated during PushJob execution.
+ */
+public interface PushCorrelationRule {
+
+    default void setConf(PushCorrelationRuleConf conf) {
+    }
+
+    /**
+     * Return a search condition.
+     *
+     * @param any user, group or any object
+     * @param provision resource provision
+     * @return search condition.
+     */
+    Filter getFilter(Any<?> any, Provision provision);
+}
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PushCorrelationRuleConfClass.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PushCorrelationRuleConfClass.java
new file mode 100644 (file)
index 0000000..deb1765
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * 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.dao;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.syncope.common.lib.policy.PushCorrelationRuleConf;
+
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface PushCorrelationRuleConfClass {
+
+    Class<? extends PushCorrelationRuleConf> value();
+
+}
index 8544cd4..05bd85a 100644 (file)
@@ -19,6 +19,7 @@
 package org.apache.syncope.core.persistence.api.entity;
 
 import java.util.Set;
+import org.apache.syncope.common.lib.patch.AnyPatch;
 import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.persistence.api.dao.AnyDAO;
@@ -48,6 +49,8 @@ public interface AnyUtils {
 
     <T extends AnyTO> T newAnyTO();
 
+    <P extends AnyPatch> P newAnyPatch(String key);
+
     <A extends Any<?>> AnyDAO<A> dao();
 
     Set<ExternalResource> getAllResources(Any<?> any);
@@ -22,11 +22,7 @@ import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.Entity;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
 
-public interface CorrelationRule extends Entity {
-
-    PullPolicy getPullPolicy();
-
-    void setPullPolicy(PullPolicy pullPolicy);
+public interface CorrelationRuleEntity extends Entity {
 
     AnyType getAnyType();
 
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/ProvisioningPolicy.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/ProvisioningPolicy.java
new file mode 100644 (file)
index 0000000..b17072d
--- /dev/null
@@ -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.policy;
+
+import org.apache.syncope.common.lib.types.ConflictResolutionAction;
+
+public interface ProvisioningPolicy extends Policy {
+
+    ConflictResolutionAction getConflictResolutionAction();
+
+    void setConflictResolutionAction(ConflictResolutionAction conflictResolutionAction);
+}
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/PullCorrelationRuleEntity.java
new file mode 100644 (file)
index 0000000..fb3f162
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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.policy;
+
+public interface PullCorrelationRuleEntity extends CorrelationRuleEntity {
+
+    PullPolicy getPullPolicy();
+
+    void setPullPolicy(PullPolicy pullPolicy);
+}
index d8d1a69..4faf109 100644 (file)
@@ -20,18 +20,13 @@ package org.apache.syncope.core.persistence.api.entity.policy;
 
 import java.util.List;
 import java.util.Optional;
-import org.apache.syncope.common.lib.types.ConflictResolutionAction;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 
-public interface PullPolicy extends Policy {
+public interface PullPolicy extends ProvisioningPolicy {
 
-    ConflictResolutionAction getConflictResolutionAction();
+    boolean add(PullCorrelationRuleEntity rule);
 
-    void setConflictResolutionAction(ConflictResolutionAction conflictResolutionAction);
+    Optional<? extends PullCorrelationRuleEntity> getCorrelationRule(AnyType anyType);
 
-    boolean add(CorrelationRule rule);
-
-    Optional<? extends CorrelationRule> getCorrelationRule(AnyType anyType);
-
-    List<? extends CorrelationRule> getCorrelationRules();
+    List<? extends PullCorrelationRuleEntity> getCorrelationRules();
 }
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
new file mode 100644 (file)
index 0000000..766b822
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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.policy;
+
+public interface PushCorrelationRuleEntity extends CorrelationRuleEntity {
+
+    PushPolicy getPushPolicy();
+
+    void setPushPolicy(PushPolicy pullPolicy);
+}
index d6819b7..9480ef5 100644 (file)
  */
 package org.apache.syncope.core.persistence.api.entity.policy;
 
-import org.apache.syncope.common.lib.policy.PushPolicySpec;
+import java.util.List;
+import java.util.Optional;
+import org.apache.syncope.core.persistence.api.entity.AnyType;
 
-public interface PushPolicy extends Policy {
+public interface PushPolicy extends ProvisioningPolicy {
 
-    PushPolicySpec getSpecification();
+    boolean add(PushCorrelationRuleEntity rule);
 
-    void setSpecification(PushPolicySpec spec);
+    Optional<? extends PushCorrelationRuleEntity> getCorrelationRule(AnyType anyType);
+
+    List<? extends PushCorrelationRuleEntity> getCorrelationRules();
 }
index c817306..0d91ed0 100644 (file)
@@ -31,6 +31,7 @@ import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.ProvidedKeyEntity;
 import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy;
 import org.identityconnectors.framework.common.objects.ObjectClass;
 
 public interface ExternalResource extends ProvidedKeyEntity {
@@ -61,6 +62,10 @@ public interface ExternalResource extends ProvidedKeyEntity {
 
     void setPullPolicy(PullPolicy pullPolicy);
 
+    PushPolicy getPushPolicy();
+
+    void setPushPolicy(PushPolicy pushPolicy);
+
     TraceLevel getCreateTraceLevel();
 
     void setCreateTraceLevel(TraceLevel createTraceLevel);
index 02cc568..502d1b0 100644 (file)
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.core.persistence.jpa.dao;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 import java.util.function.Function;
 import java.util.stream.Collectors;
@@ -54,13 +56,13 @@ public class DefaultPullCorrelationRule implements PullCorrelationRule {
                 collect(Collectors.toMap(Item::getIntAttrName, Function.identity()));
 
         // search for anys by attribute(s) specified in the policy
-        SearchCond searchCond = null;
+        List<SearchCond> searchConds = new ArrayList<>();
 
-        for (String schema : conf.getSchemas()) {
-            Item mappingItem = mappingItems.get(schema);
-            Attribute attr = mappingItem == null
+        conf.getSchemas().forEach(schema -> {
+            Item item = mappingItems.get(schema);
+            Attribute attr = item == null
                     ? null
-                    : connObj.getAttributeByName(mappingItem.getExtAttrName());
+                    : connObj.getAttributeByName(item.getExtAttrName());
             if (attr == null) {
                 throw new IllegalArgumentException(
                         "Connector object does not contains the attributes to perform the search: " + schema);
@@ -80,34 +82,19 @@ public class DefaultPullCorrelationRule implements PullCorrelationRule {
                         : attr.getValue().get(0).toString();
             }
 
-            SearchCond nodeCond;
-            // users: just key or username can be selected
-            // groups: just key or name can be selected
-            // any objects: just key or name can be selected
-            if ("key".equalsIgnoreCase(schema)
-                    || "username".equalsIgnoreCase(schema) || "name".equalsIgnoreCase(schema)) {
+            AttributeCond cond = "key".equalsIgnoreCase(schema)
+                    || "username".equalsIgnoreCase(schema) || "name".equalsIgnoreCase(schema)
+                    ? new AnyCond()
+                    : new AttributeCond();
+            cond.setSchema(schema);
+            cond.setType(type);
+            cond.setExpression(expression);
 
-                AnyCond cond = new AnyCond();
-                cond.setSchema(schema);
-                cond.setType(type);
-                cond.setExpression(expression);
+            searchConds.add(SearchCond.getLeafCond(cond));
+        });
 
-                nodeCond = SearchCond.getLeafCond(cond);
-            } else {
-                AttributeCond cond = new AttributeCond();
-                cond.setSchema(schema);
-                cond.setType(type);
-                cond.setExpression(expression);
-
-                nodeCond = SearchCond.getLeafCond(cond);
-            }
-
-            searchCond = searchCond == null
-                    ? nodeCond
-                    : SearchCond.getAndCond(searchCond, nodeCond);
-        }
-
-        return searchCond;
+        return conf.isOrSchemas()
+                ? SearchCond.getOrCond(searchConds)
+                : SearchCond.getAndCond(searchConds);
     }
-
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPushCorrelationRule.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPushCorrelationRule.java
new file mode 100644 (file)
index 0000000..1583899
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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.dao;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.policy.DefaultPushCorrelationRuleConf;
+import org.apache.syncope.common.lib.policy.PushCorrelationRuleConf;
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.syncope.core.persistence.api.entity.resource.Provision;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.apache.syncope.core.persistence.api.dao.PushCorrelationRule;
+import org.apache.syncope.core.persistence.api.dao.PushCorrelationRuleConfClass;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.provisioning.api.MappingManager;
+import org.identityconnectors.framework.common.objects.filter.Filter;
+import org.identityconnectors.framework.common.objects.filter.FilterBuilder;
+import org.springframework.beans.factory.annotation.Autowired;
+
+@PushCorrelationRuleConfClass(DefaultPushCorrelationRuleConf.class)
+public class DefaultPushCorrelationRule implements PushCorrelationRule {
+
+    @Autowired
+    private MappingManager mappingManager;
+
+    private DefaultPushCorrelationRuleConf conf;
+
+    @Override
+    public void setConf(final PushCorrelationRuleConf conf) {
+        if (conf instanceof DefaultPushCorrelationRuleConf) {
+            this.conf = DefaultPushCorrelationRuleConf.class.cast(conf);
+        } else {
+            throw new IllegalArgumentException(
+                    DefaultPushCorrelationRuleConf.class.getName() + " expected, got " + conf.getClass().getName());
+        }
+    }
+
+    @Override
+    public Filter getFilter(final Any<?> any, final Provision provision) {
+        List<Filter> filters = new ArrayList<>();
+
+        provision.getMapping().getItems().stream().filter(
+                item -> item.getPurpose() == MappingPurpose.PROPAGATION || item.getPurpose() == MappingPurpose.BOTH).
+                forEach(item -> {
+                    Pair<String, Attribute> attr = mappingManager.prepareAttr(provision, item, any, null);
+                    if (attr != null && attr.getRight() != null && conf.getSchemas().contains(item.getIntAttrName())) {
+                        filters.add(provision.isIgnoreCaseMatch()
+                                ? FilterBuilder.equalsIgnoreCase(attr.getRight())
+                                : FilterBuilder.equalTo(attr.getRight()));
+                    }
+                });
+
+        return conf.isOrSchemas()
+                ? FilterBuilder.or(filters)
+                : FilterBuilder.and(filters);
+    }
+}
index cc5bf94..dd839a3 100644 (file)
@@ -77,7 +77,7 @@ public class HaveIBeenPwnedPasswordRule implements PasswordRule {
 
         if (password != null && clearPassword != null) {
             try {
-                final String sha1 = ENCRYPTOR.encode(clearPassword, CipherAlgorithm.SHA1);
+                String sha1 = ENCRYPTOR.encode(clearPassword, CipherAlgorithm.SHA1);
 
                 HttpHeaders headers = new HttpHeaders();
                 headers.set(HttpHeaders.USER_AGENT, "Apache Syncope");
index efcd24a..9c573fd 100644 (file)
@@ -45,6 +45,7 @@ import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
 import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.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;
 import org.apache.syncope.core.persistence.api.entity.resource.Provision;
 import org.apache.syncope.core.persistence.jpa.entity.resource.JPAMappingItem;
 import org.apache.syncope.core.persistence.jpa.entity.resource.JPAExternalResource;
@@ -227,6 +228,8 @@ public class JPAExternalResourceDAO extends AbstractDAO<ExternalResource> implem
             query.append("passwordPolicy");
         } else if (PullPolicy.class.isAssignableFrom(policyClass)) {
             query.append("pullPolicy");
+        } else if (PushPolicy.class.isAssignableFrom(policyClass)) {
+            query.append("pushPolicy");
         }
 
         return query;
index 52bca60..495585f 100644 (file)
@@ -20,21 +20,22 @@ package org.apache.syncope.core.persistence.jpa.dao;
 
 import java.util.List;
 import javax.persistence.TypedQuery;
+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.dao.RealmDAO;
 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.resource.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
-import org.apache.syncope.core.persistence.api.entity.Realm;
 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;
 import org.apache.syncope.core.persistence.jpa.entity.policy.AbstractPolicy;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccountPolicy;
-import org.apache.syncope.core.persistence.jpa.entity.policy.JPACorrelationRule;
+import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPullCorrelationRuleEntity;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPasswordPolicy;
 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.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Repository;
@@ -45,6 +46,9 @@ public class JPAPolicyDAO extends AbstractDAO<Policy> implements PolicyDAO {
     @Autowired
     private RealmDAO realmDAO;
 
+    @Autowired
+    private ExternalResourceDAO resourceDAO;
+
     private <T extends Policy> Class<? extends AbstractPolicy> getEntityReference(final Class<T> reference) {
         return AccountPolicy.class.isAssignableFrom(reference)
                 ? JPAAccountPolicy.class
@@ -92,9 +96,9 @@ public class JPAPolicyDAO extends AbstractDAO<Policy> implements PolicyDAO {
     }
 
     @Override
-    public List<PullPolicy> findByCorrelationRule(final Implementation correlationRule) {
+    public List<PullPolicy> findByPullCorrelationRule(final Implementation correlationRule) {
         TypedQuery<PullPolicy> query = entityManager().createQuery(
-                "SELECT DISTINCT e.pullPolicy FROM " + JPACorrelationRule.class.getSimpleName() + " e "
+                "SELECT DISTINCT e.pullPolicy FROM " + JPAPullCorrelationRuleEntity.class.getSimpleName() + " e "
                 + "WHERE e.implementation=:correlationRule", PullPolicy.class);
         query.setParameter("correlationRule", correlationRule);
 
@@ -102,6 +106,16 @@ public class JPAPolicyDAO extends AbstractDAO<Policy> implements PolicyDAO {
     }
 
     @Override
+    public List<PushPolicy> findByPushCorrelationRule(final Implementation correlationRule) {
+        TypedQuery<PushPolicy> query = entityManager().createQuery(
+                "SELECT DISTINCT e.pushPolicy FROM " + JPAPushCorrelationRuleEntity.class.getSimpleName() + " e "
+                + "WHERE e.implementation=:correlationRule", PushPolicy.class);
+        query.setParameter("correlationRule", correlationRule);
+
+        return query.getResultList();
+    }
+
+    @Override
     public List<AccountPolicy> findByResource(final ExternalResource resource) {
         TypedQuery<AccountPolicy> query = entityManager().createQuery(
                 "SELECT e FROM " + JPAAccountPolicy.class.getSimpleName() + " e "
@@ -125,13 +139,25 @@ public class JPAPolicyDAO extends AbstractDAO<Policy> implements PolicyDAO {
 
     @Override
     public <T extends Policy> void delete(final T policy) {
-        for (Realm realm : realmDAO.findByPolicy(policy)) {
+        realmDAO.findByPolicy(policy).forEach(realm -> {
             if (policy instanceof AccountPolicy) {
                 realm.setAccountPolicy(null);
             } else if (policy instanceof PasswordPolicy) {
                 realm.setPasswordPolicy(null);
             }
-        }
+        });
+
+        resourceDAO.findByPolicy(policy).forEach(resource -> {
+            if (policy instanceof AccountPolicy) {
+                resource.setAccountPolicy(null);
+            } else if (policy instanceof PasswordPolicy) {
+                resource.setPasswordPolicy(null);
+            } else if (policy instanceof PullPolicy) {
+                resource.setPullPolicy(null);
+            } else if (policy instanceof PushPolicy) {
+                resource.setPushPolicy(null);
+            }
+        });
 
         entityManager().remove(policy);
     }
index 7f90046..b2ad416 100644 (file)
@@ -34,7 +34,7 @@ import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 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.ProvisioningPolicy;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.persistence.jpa.entity.JPARealm;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -134,7 +134,7 @@ public class JPARealmDAO extends AbstractDAO<Realm> implements RealmDAO {
 
     @Override
     public <T extends Policy> List<Realm> findByPolicy(final T policy) {
-        if (PullPolicy.class.isAssignableFrom(policy.getClass())) {
+        if (ProvisioningPolicy.class.isAssignableFrom(policy.getClass())) {
             return Collections.<Realm>emptyList();
         }
 
@@ -234,5 +234,4 @@ public class JPARealmDAO extends AbstractDAO<Realm> implements RealmDAO {
 
         delete(realm);
     }
-
 }
index 67eda83..5e97993 100644 (file)
@@ -514,7 +514,6 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
     @Transactional(readOnly = true)
     @Override
     public Collection<String> findAllResourceKeys(final String key) {
-        return findAllResources(authFind(key)).stream().map(resource -> resource.getKey()).collect(Collectors.toList());
+        return findAllResources(authFind(key)).stream().map(Entity::getKey).collect(Collectors.toList());
     }
-
 }
index d4a3f60..70f62c6 100644 (file)
@@ -26,6 +26,10 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import org.apache.commons.lang3.ClassUtils;
+import org.apache.syncope.common.lib.patch.AnyObjectPatch;
+import org.apache.syncope.common.lib.patch.AnyPatch;
+import org.apache.syncope.common.lib.patch.GroupPatch;
+import org.apache.syncope.common.lib.patch.UserPatch;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.to.GroupTO;
@@ -344,6 +348,33 @@ public class JPAAnyUtils implements AnyUtils {
     }
 
     @Override
+    public <P extends AnyPatch> P newAnyPatch(final String key) {
+        P result = null;
+
+        switch (anyTypeKind) {
+            case USER:
+                result = (P) new UserPatch();
+                break;
+
+            case GROUP:
+                result = (P) new GroupPatch();
+                break;
+
+            case ANY_OBJECT:
+                result = (P) new AnyObjectPatch();
+                break;
+
+            default:
+        }
+
+        if (result != null) {
+            result.setKey(key);
+        }
+
+        return result;
+    }
+
+    @Override
     public <A extends Any<?>> AnyDAO<A> dao() {
         AnyDAO<A> result = null;
 
index 8de177e..0ec1538 100644 (file)
@@ -131,12 +131,14 @@ import org.apache.syncope.core.persistence.api.entity.DynRealmMembership;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.Privilege;
 import org.apache.syncope.core.persistence.api.entity.Remediation;
-import org.apache.syncope.core.persistence.api.entity.policy.CorrelationRule;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResourceHistoryConf;
 import org.apache.syncope.core.persistence.api.entity.resource.OrgUnitItem;
-import org.apache.syncope.core.persistence.jpa.entity.policy.JPACorrelationRule;
+import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPullCorrelationRuleEntity;
 import org.apache.syncope.core.persistence.jpa.entity.resource.JPAExternalResourceHistoryConf;
 import org.apache.syncope.core.persistence.jpa.entity.resource.JPAOrgUnitItem;
+import org.apache.syncope.core.persistence.api.entity.policy.PullCorrelationRuleEntity;
+import org.apache.syncope.core.persistence.api.entity.policy.PushCorrelationRuleEntity;
+import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPushCorrelationRuleEntity;
 
 @Component
 public class JPAEntityFactory implements EntityFactory {
@@ -164,8 +166,10 @@ public class JPAEntityFactory implements EntityFactory {
             result = (E) new JPAPushPolicy();
         } else if (reference.equals(PullPolicy.class)) {
             result = (E) new JPAPullPolicy();
-        } else if (reference.equals(CorrelationRule.class)) {
-            result = (E) new JPACorrelationRule();
+        } else if (reference.equals(PullCorrelationRuleEntity.class)) {
+            result = (E) new JPAPullCorrelationRuleEntity();
+        } else if (reference.equals(PushCorrelationRuleEntity.class)) {
+            result = (E) new JPAPushCorrelationRuleEntity();
         } else if (reference.equals(AnyTypeClass.class)) {
             result = (E) new JPAAnyTypeClass();
         } else if (reference.equals(AnyType.class)) {
  */
 package org.apache.syncope.core.persistence.jpa.entity.policy;
 
-import javax.persistence.Entity;
 import javax.persistence.ManyToOne;
-import javax.persistence.Table;
-import javax.persistence.UniqueConstraint;
+import javax.persistence.MappedSuperclass;
 import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
-import org.apache.syncope.core.persistence.api.entity.policy.CorrelationRule;
-import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.CorrelationRuleEntity;
 import org.apache.syncope.core.persistence.jpa.entity.AbstractGeneratedKeyEntity;
 import org.apache.syncope.core.persistence.jpa.entity.JPAAnyType;
 import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation;
 
-@Entity
-@Table(name = JPACorrelationRule.TABLE, uniqueConstraints =
-        @UniqueConstraint(columnNames = { "pullPolicy_id", "anyType_id" }))
-public class JPACorrelationRule extends AbstractGeneratedKeyEntity implements CorrelationRule {
+@MappedSuperclass
+abstract class AbstractCorrelationRuleEntity extends AbstractGeneratedKeyEntity implements CorrelationRuleEntity {
 
-    private static final long serialVersionUID = 4276417265524083919L;
-
-    public static final String TABLE = "CorrelationRule";
-
-    @ManyToOne(optional = false)
-    private JPAPullPolicy pullPolicy;
+    private static final long serialVersionUID = 4017405130146577834L;
 
     @ManyToOne(optional = false)
     private JPAAnyType anyType;
@@ -50,17 +40,6 @@ public class JPACorrelationRule extends AbstractGeneratedKeyEntity implements Co
     private JPAImplementation implementation;
 
     @Override
-    public PullPolicy getPullPolicy() {
-        return pullPolicy;
-    }
-
-    @Override
-    public void setPullPolicy(final PullPolicy pullPolicy) {
-        checkType(pullPolicy, JPAPullPolicy.class);
-        this.pullPolicy = (JPAPullPolicy) pullPolicy;
-    }
-
-    @Override
     public AnyType getAnyType() {
         return anyType;
     }
@@ -76,11 +55,12 @@ public class JPACorrelationRule extends AbstractGeneratedKeyEntity implements Co
         return implementation;
     }
 
+    protected abstract ImplementationType getImplementationType();
+
     @Override
     public void setImplementation(final Implementation implementation) {
         checkType(implementation, JPAImplementation.class);
-        checkImplementationType(implementation, ImplementationType.PULL_CORRELATION_RULE);
+        checkImplementationType(implementation, getImplementationType());
         this.implementation = (JPAImplementation) implementation;
     }
-
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/AbstractProvisioningPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/AbstractProvisioningPolicy.java
new file mode 100644 (file)
index 0000000..ffdb008
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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.policy;
+
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.MappedSuperclass;
+import javax.validation.constraints.NotNull;
+import org.apache.syncope.common.lib.types.ConflictResolutionAction;
+import org.apache.syncope.core.persistence.api.entity.policy.ProvisioningPolicy;
+
+@MappedSuperclass
+abstract class AbstractProvisioningPolicy extends AbstractPolicy implements ProvisioningPolicy {
+
+    private static final long serialVersionUID = 3804545832315575686L;
+
+    @Enumerated(EnumType.STRING)
+    @NotNull
+    private ConflictResolutionAction conflictResolutionAction;
+
+    @Override
+    public ConflictResolutionAction getConflictResolutionAction() {
+        return conflictResolutionAction;
+    }
+
+    @Override
+    public void setConflictResolutionAction(final ConflictResolutionAction conflictResolutionAction) {
+        this.conflictResolutionAction = conflictResolutionAction;
+    }
+}
index 6ecd1d0..2a616c2 100644 (file)
@@ -22,6 +22,7 @@ import org.apache.syncope.common.lib.policy.AccountPolicyTO;
 import org.apache.syncope.common.lib.policy.PasswordPolicyTO;
 import org.apache.syncope.common.lib.policy.PolicyTO;
 import org.apache.syncope.common.lib.policy.PullPolicyTO;
+import org.apache.syncope.common.lib.policy.PushPolicyTO;
 import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
@@ -67,6 +68,8 @@ public class JPAPolicyUtilsFactory implements PolicyUtilsFactory {
             type = PolicyType.PASSWORD;
         } else if (policyClass == PullPolicyTO.class) {
             type = PolicyType.PULL;
+        } else if (policyClass == PushPolicyTO.class) {
+            type = PolicyType.PUSH;
         } else {
             throw new IllegalArgumentException("Invalid PolicyTO class: " + policyClass.getName());
         }
@@ -78,5 +81,4 @@ public class JPAPolicyUtilsFactory implements PolicyUtilsFactory {
     public PolicyUtils getInstance(final PolicyTO policyTO) {
         return getInstance(policyTO.getClass());
     }
-
 }
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/JPAPullCorrelationRuleEntity.java
new file mode 100644 (file)
index 0000000..394ad08
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.entity.policy;
+
+import javax.persistence.Entity;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import org.apache.syncope.common.lib.types.ImplementationType;
+import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.PullCorrelationRuleEntity;
+
+@Entity
+@Table(name = JPAPullCorrelationRuleEntity.TABLE, uniqueConstraints =
+        @UniqueConstraint(columnNames = { "pullPolicy_id", "anyType_id" }))
+public class JPAPullCorrelationRuleEntity extends AbstractCorrelationRuleEntity implements PullCorrelationRuleEntity {
+
+    private static final long serialVersionUID = 4276417265524083919L;
+
+    public static final String TABLE = "PullCorrelationRuleEntity";
+
+    @ManyToOne(optional = false)
+    private JPAPullPolicy pullPolicy;
+
+    @Override
+    protected ImplementationType getImplementationType() {
+        return ImplementationType.PULL_CORRELATION_RULE;
+    }
+
+    @Override
+    public PullPolicy getPullPolicy() {
+        return pullPolicy;
+    }
+
+    @Override
+    public void setPullPolicy(final PullPolicy pullPolicy) {
+        checkType(pullPolicy, JPAPullPolicy.class);
+        this.pullPolicy = (JPAPullPolicy) pullPolicy;
+    }
+}
index b7d0720..076695a 100644 (file)
@@ -23,56 +23,38 @@ import java.util.List;
 import java.util.Optional;
 import javax.persistence.CascadeType;
 import javax.persistence.Entity;
-import javax.persistence.EnumType;
-import javax.persistence.Enumerated;
 import javax.persistence.FetchType;
 import javax.persistence.OneToMany;
 import javax.persistence.Table;
-import javax.validation.constraints.NotNull;
-import org.apache.syncope.common.lib.types.ConflictResolutionAction;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
-import org.apache.syncope.core.persistence.api.entity.policy.CorrelationRule;
 import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.PullCorrelationRuleEntity;
 
 @Entity
 @Table(name = JPAPullPolicy.TABLE)
-public class JPAPullPolicy extends AbstractPolicy implements PullPolicy {
+public class JPAPullPolicy extends AbstractProvisioningPolicy implements PullPolicy {
 
     private static final long serialVersionUID = -6090413855809521279L;
 
     public static final String TABLE = "PullPolicy";
 
-    @Enumerated(EnumType.STRING)
-    @NotNull
-    private ConflictResolutionAction conflictResolutionAction;
-
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "pullPolicy")
-    private List<JPACorrelationRule> correlationRules = new ArrayList<>();
-
-    @Override
-    public ConflictResolutionAction getConflictResolutionAction() {
-        return conflictResolutionAction;
-    }
-
-    @Override
-    public void setConflictResolutionAction(final ConflictResolutionAction conflictResolutionAction) {
-        this.conflictResolutionAction = conflictResolutionAction;
-    }
+    private List<JPAPullCorrelationRuleEntity> correlationRules = new ArrayList<>();
 
     @Override
-    public boolean add(final CorrelationRule filter) {
-        checkType(filter, JPACorrelationRule.class);
-        return this.correlationRules.add((JPACorrelationRule) filter);
+    public boolean add(final PullCorrelationRuleEntity filter) {
+        checkType(filter, JPAPullCorrelationRuleEntity.class);
+        return this.correlationRules.add((JPAPullCorrelationRuleEntity) filter);
     }
 
     @Override
-    public Optional<? extends CorrelationRule> getCorrelationRule(final AnyType anyType) {
+    public Optional<? extends PullCorrelationRuleEntity> getCorrelationRule(final AnyType anyType) {
         return correlationRules.stream().
                 filter(rule -> anyType != null && anyType.equals(rule.getAnyType())).findFirst();
     }
 
     @Override
-    public List<? extends CorrelationRule> getCorrelationRules() {
+    public List<? extends PullCorrelationRuleEntity> getCorrelationRules() {
         return correlationRules;
     }
 }
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPushCorrelationRuleEntity.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPushCorrelationRuleEntity.java
new file mode 100644 (file)
index 0000000..bdf05a7
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.entity.policy;
+
+import javax.persistence.Entity;
+import javax.persistence.ManyToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+import org.apache.syncope.common.lib.types.ImplementationType;
+import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.PushCorrelationRuleEntity;
+
+@Entity
+@Table(name = JPAPushCorrelationRuleEntity.TABLE, uniqueConstraints =
+        @UniqueConstraint(columnNames = { "pushPolicy_id", "anyType_id" }))
+public class JPAPushCorrelationRuleEntity extends AbstractCorrelationRuleEntity implements PushCorrelationRuleEntity {
+
+    private static final long serialVersionUID = 4276417265524083919L;
+
+    public static final String TABLE = "PushCorrelationRuleEntity";
+
+    @ManyToOne(optional = false)
+    private JPAPushPolicy pushPolicy;
+
+    @Override
+    protected ImplementationType getImplementationType() {
+        return ImplementationType.PUSH_CORRELATION_RULE;
+    }
+
+    @Override
+    public PushPolicy getPushPolicy() {
+        return pushPolicy;
+    }
+
+    @Override
+    public void setPushPolicy(final PushPolicy pushPolicy) {
+        checkType(pushPolicy, JPAPushPolicy.class);
+        this.pushPolicy = (JPAPushPolicy) pushPolicy;
+    }
+}
index dcdfc1d..5103647 100644 (file)
  */
 package org.apache.syncope.core.persistence.jpa.entity.policy;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import javax.persistence.CascadeType;
 import javax.persistence.Entity;
-import javax.persistence.Lob;
+import javax.persistence.FetchType;
+import javax.persistence.OneToMany;
 import javax.persistence.Table;
-import org.apache.syncope.common.lib.policy.PushPolicySpec;
-import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.PushCorrelationRuleEntity;
 
 @Entity
 @Table(name = JPAPushPolicy.TABLE)
-public class JPAPushPolicy extends AbstractPolicy implements PushPolicy {
+public class JPAPushPolicy extends AbstractProvisioningPolicy implements PushPolicy {
 
     private static final long serialVersionUID = -5875589156893921113L;
 
     public static final String TABLE = "PushPolicy";
 
-    @Lob
-    private String specification;
+    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "pushPolicy")
+    private List<JPAPushCorrelationRuleEntity> correlationRules = new ArrayList<>();
 
     @Override
-    public PushPolicySpec getSpecification() {
-        return POJOHelper.deserialize(specification, PushPolicySpec.class);
+    public boolean add(final PushCorrelationRuleEntity filter) {
+        checkType(filter, JPAPushCorrelationRuleEntity.class);
+        return this.correlationRules.add((JPAPushCorrelationRuleEntity) filter);
     }
 
     @Override
-    public void setSpecification(final PushPolicySpec policy) {
-        this.specification = POJOHelper.serialize(policy);
+    public Optional<? extends PushCorrelationRuleEntity> getCorrelationRule(final AnyType anyType) {
+        return correlationRules.stream().
+                filter(rule -> anyType != null && anyType.equals(rule.getAnyType())).findFirst();
     }
 
+    @Override
+    public List<? extends PushCorrelationRuleEntity> getCorrelationRules() {
+        return correlationRules;
+    }
 }
index 6335bcf..b8e218f 100644 (file)
@@ -64,9 +64,11 @@ import org.apache.syncope.core.persistence.jpa.entity.JPAConnInstance;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPasswordPolicy;
 import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPullPolicy;
 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.resource.OrgUnit;
 import org.apache.syncope.core.persistence.jpa.entity.AbstractProvidedKeyEntity;
 import org.apache.syncope.core.persistence.jpa.entity.JPAImplementation;
+import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPushPolicy;
 import org.identityconnectors.framework.common.objects.ObjectClass;
 
 /**
@@ -141,6 +143,9 @@ public class JPAExternalResource extends AbstractProvidedKeyEntity implements Ex
     @ManyToOne(fetch = FetchType.EAGER)
     private JPAPullPolicy pullPolicy;
 
+    @ManyToOne(fetch = FetchType.EAGER)
+    private JPAPushPolicy pushPolicy;
+
     /**
      * Configuration properties that are overridden from the connector instance.
      */
@@ -338,6 +343,17 @@ public class JPAExternalResource extends AbstractProvidedKeyEntity implements Ex
     }
 
     @Override
+    public PushPolicy getPushPolicy() {
+        return pushPolicy;
+    }
+
+    @Override
+    public void setPushPolicy(final PushPolicy pushPolicy) {
+        checkType(pushPolicy, JPAPushPolicy.class);
+        this.pushPolicy = (JPAPushPolicy) pushPolicy;
+    }
+
+    @Override
     public Set<ConnConfProperty> getConfOverride() {
         Set<ConnConfProperty> confOverride = new HashSet<>();
         if (!StringUtils.isBlank(jsonConf)) {
index 5b47a75..376534c 100644 (file)
@@ -23,16 +23,19 @@ import java.util.Set;
 import org.apache.syncope.common.lib.policy.AccountRuleConf;
 import org.apache.syncope.common.lib.policy.PasswordRuleConf;
 import org.apache.syncope.common.lib.policy.PullCorrelationRuleConf;
+import org.apache.syncope.common.lib.policy.PushCorrelationRuleConf;
 import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.core.persistence.api.ImplementationLookup;
 import org.apache.syncope.core.persistence.api.dao.AccountRule;
 import org.apache.syncope.core.persistence.api.dao.PasswordRule;
 import org.apache.syncope.core.persistence.api.dao.PullCorrelationRule;
+import org.apache.syncope.core.persistence.api.dao.PushCorrelationRule;
 import org.apache.syncope.core.persistence.api.dao.Reportlet;
 import org.apache.syncope.core.persistence.jpa.dao.DefaultAccountRule;
 import org.apache.syncope.core.persistence.jpa.dao.DefaultPasswordRule;
 import org.apache.syncope.core.persistence.jpa.dao.DefaultPullCorrelationRule;
+import org.apache.syncope.core.persistence.jpa.dao.DefaultPushCorrelationRule;
 import org.springframework.stereotype.Component;
 
 @Component
@@ -87,6 +90,13 @@ public class DummyImplementationLookup implements ImplementationLookup {
     }
 
     @Override
+    public Class<? extends PushCorrelationRule> getPushCorrelationRuleClass(
+            final Class<? extends PushCorrelationRuleConf> pushCorrelationRuleConfClass) {
+
+        return DefaultPushCorrelationRule.class;
+    }
+
+    @Override
     public Set<Class<?>> getAuditAppenderClasses() {
         return Collections.emptySet();
     }
index c0982e6..2f29e17 100644 (file)
@@ -43,7 +43,7 @@ public class ImplementationTest extends AbstractTest {
         List<Implementation> implementations = implementationDAO.findAll();
         assertFalse(implementations.isEmpty());
 
-        assertEquals(17, implementations.size());
+        assertEquals(18, implementations.size());
 
         implementations = implementationDAO.find(ImplementationType.PULL_ACTIONS);
         assertEquals(1, implementations.size());
@@ -68,6 +68,9 @@ public class ImplementationTest extends AbstractTest {
 
         implementations = implementationDAO.find(ImplementationType.PULL_CORRELATION_RULE);
         assertEquals(1, implementations.size());
+
+        implementations = implementationDAO.find(ImplementationType.PUSH_CORRELATION_RULE);
+        assertEquals(1, implementations.size());
     }
 
     @Test
index 2fa7345..6a6b16c 100644 (file)
@@ -28,6 +28,7 @@ import java.util.List;
 import java.util.UUID;
 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.types.ConflictResolutionAction;
 import org.apache.syncope.common.lib.types.ImplementationEngine;
 import org.apache.syncope.common.lib.types.ImplementationType;
@@ -37,7 +38,6 @@ import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
 import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
-import org.apache.syncope.core.persistence.api.entity.policy.CorrelationRule;
 import org.apache.syncope.core.persistence.api.entity.policy.Policy;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
 import org.junit.jupiter.api.Test;
@@ -45,6 +45,9 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
 import org.apache.syncope.core.persistence.api.entity.policy.PullPolicy;
 import org.apache.syncope.core.persistence.api.dao.PullCorrelationRule;
+import org.apache.syncope.core.persistence.api.entity.policy.PullCorrelationRuleEntity;
+import org.apache.syncope.core.persistence.api.entity.policy.PushCorrelationRuleEntity;
+import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy;
 
 @Transactional("Master")
 public class PolicyTest extends AbstractTest {
@@ -67,17 +70,28 @@ public class PolicyTest extends AbstractTest {
 
     @Test
     public void findByKey() {
-        PullPolicy policy = policyDAO.find("880f8553-069b-4aed-9930-2cd53873f544");
-        assertNotNull(policy);
-
-        CorrelationRule rule = policy.getCorrelationRule(anyTypeDAO.findUser()).orElse(null);
-        assertNotNull(rule);
-        DefaultPullCorrelationRuleConf ruleConf =
-                POJOHelper.deserialize(rule.getImplementation().getBody(), DefaultPullCorrelationRuleConf.class);
-        assertNotNull(ruleConf);
-        assertEquals(2, ruleConf.getSchemas().size());
-        assertTrue(ruleConf.getSchemas().contains("username"));
-        assertTrue(ruleConf.getSchemas().contains("firstname"));
+        PullPolicy pullPolicy = policyDAO.find("880f8553-069b-4aed-9930-2cd53873f544");
+        assertNotNull(pullPolicy);
+
+        PullCorrelationRuleEntity pullCR = pullPolicy.getCorrelationRule(anyTypeDAO.findUser()).orElse(null);
+        assertNotNull(pullCR);
+        DefaultPullCorrelationRuleConf pullCRConf =
+                POJOHelper.deserialize(pullCR.getImplementation().getBody(), DefaultPullCorrelationRuleConf.class);
+        assertNotNull(pullCRConf);
+        assertEquals(2, pullCRConf.getSchemas().size());
+        assertTrue(pullCRConf.getSchemas().contains("username"));
+        assertTrue(pullCRConf.getSchemas().contains("firstname"));
+
+        PushPolicy pushPolicy = policyDAO.find("fb6530e5-892d-4f47-a46b-180c5b6c5c83");
+        assertNotNull(pushPolicy);
+
+        PushCorrelationRuleEntity pushCR = pushPolicy.getCorrelationRule(anyTypeDAO.findUser()).orElse(null);
+        assertNotNull(pushCR);
+        DefaultPushCorrelationRuleConf pushCRConf =
+                POJOHelper.deserialize(pushCR.getImplementation().getBody(), DefaultPushCorrelationRuleConf.class);
+        assertNotNull(pushCRConf);
+        assertEquals(1, pushCRConf.getSchemas().size());
+        assertTrue(pushCRConf.getSchemas().contains("email"));
     }
 
     @Test
@@ -103,7 +117,7 @@ public class PolicyTest extends AbstractTest {
         impl1.setBody(PullCorrelationRule.class.getName());
         impl1 = implementationDAO.save(impl1);
 
-        CorrelationRule rule1 = entityFactory.newEntity(CorrelationRule.class);
+        PullCorrelationRuleEntity rule1 = entityFactory.newEntity(PullCorrelationRuleEntity.class);
         rule1.setAnyType(anyTypeDAO.findUser());
         rule1.setPullPolicy(policy);
         rule1.setImplementation(impl1);
@@ -116,7 +130,7 @@ public class PolicyTest extends AbstractTest {
         impl2.setBody(PullCorrelationRule.class.getName());
         impl2 = implementationDAO.save(impl2);
 
-        CorrelationRule rule2 = entityFactory.newEntity(CorrelationRule.class);
+        PullCorrelationRuleEntity rule2 = entityFactory.newEntity(PullCorrelationRuleEntity.class);
         rule2.setAnyType(anyTypeDAO.findGroup());
         rule2.setPullPolicy(policy);
         rule2.setImplementation(impl2);
index 3243cf2..3c33586 100644 (file)
@@ -640,11 +640,18 @@ under the License.
   <PullPolicy id="880f8553-069b-4aed-9930-2cd53873f544" description="another pull policy" conflictResolutionAction="ALL"/>
   <Implementation id="TestPullCorrelationRule" type="PULL_CORRELATION_RULE" engine="JAVA"
                   body='{"@class":"org.apache.syncope.common.lib.policy.DefaultPullCorrelationRuleConf","name":"org.apache.syncope.common.lib.policy.DefaultPullCorrelationRuleConf","schemas":["username","firstname"]}'/>
-  <CorrelationRule id="10e3d196-7486-4c88-aefd-59e40d93a0c1" pullPolicy_id="880f8553-069b-4aed-9930-2cd53873f544" 
-                   anyType_id="USER" implementation_id="TestPullCorrelationRule"/>
+  <PullCorrelationRuleEntity id="10e3d196-7486-4c88-aefd-59e40d93a0c1" pullPolicy_id="880f8553-069b-4aed-9930-2cd53873f544" 
+                             anyType_id="USER" implementation_id="TestPullCorrelationRule"/>
   <PullPolicy id="4ad10d94-e002-4b3f-b771-16089cc71da9" description="pull policy 1" conflictResolutionAction="IGNORE"/>
   <PullPolicy id="9454b0d7-2610-400a-be82-fc23cf553dd6" description="pull policy for java rule" conflictResolutionAction="IGNORE"/>
 
+  <!-- push policies -->
+  <PushPolicy id="fb6530e5-892d-4f47-a46b-180c5b6c5c83" description="a push policy" conflictResolutionAction="IGNORE"/>
+  <Implementation id="TestPushCorrelationRule" type="PUSH_CORRELATION_RULE" engine="JAVA"
+                  body='{"@class":"org.apache.syncope.common.lib.policy.DefaultPushCorrelationRuleConf","name":"org.apache.syncope.common.lib.policy.DefaultPushCorrelationRuleConf","schemas":["email"]}'/>
+  <PushCorrelationRuleEntity id="24463935-32a0-4272-bc78-04d6d0adc69e" pushPolicy_id="fb6530e5-892d-4f47-a46b-180c5b6c5c83" 
+                             anyType_id="USER" implementation_id="TestPushCorrelationRule"/>
+  
   <ConnInstance id="88a7a819-dab5-46b4-9b90-0b9769eabdb8" displayName="ConnInstance100"
                 adminRealm_id="e4c28e7a-9dbf-4ee7-9441-93812a0d4a28"
                 location="${connid.location}"
index e5d23a9..1cc30c6 100644 (file)
@@ -65,6 +65,17 @@ public interface MappingManager {
     List<PlainAttrValue> getIntValues(Provision provision, Item mapItem, IntAttrName intAttrName, Any<?> any);
 
     /**
+     * Prepare attribute for sending to a connector instance.
+     *
+     * @param provision provision information
+     * @param item mapping item
+     * @param any given any object
+     * @param password clear-text password
+     * @return connObjectLink (if it is the case) + prepared attribute
+     */
+    Pair<String, Attribute> prepareAttr(Provision provision, Item item, Any<?> any, String password);
+
+    /**
      * Prepare attributes for sending to a connector instance.
      *
      * @param any given any object
index 4c02da1..9aa3050 100644 (file)
@@ -34,7 +34,7 @@ public class ProvisioningProfile<T extends ProvisioningTask, A extends Provision
 
     private boolean dryRun;
 
-    private ConflictResolutionAction resAct;
+    private ConflictResolutionAction conflictResolutionAction;
 
     private final List<A> actions = new ArrayList<>();
 
@@ -63,12 +63,12 @@ public class ProvisioningProfile<T extends ProvisioningTask, A extends Provision
         this.dryRun = dryRun;
     }
 
-    public ConflictResolutionAction getResAct() {
-        return resAct;
+    public ConflictResolutionAction getConflictResolutionAction() {
+        return conflictResolutionAction;
     }
 
-    public void setResAct(final ConflictResolutionAction resAct) {
-        this.resAct = resAct;
+    public void setConflictResolutionAction(final ConflictResolutionAction conflictResolutionAction) {
+        this.conflictResolutionAction = conflictResolutionAction;
     }
 
     public List<A> getActions() {
index 5186a70..fbaf761 100644 (file)
@@ -304,23 +304,18 @@ public class MappingManagerImpl implements MappingManager {
         return Pair.of(connObjectKey, attributes);
     }
 
-    /**
-     * Prepare an attribute to be sent to a connector instance.
-     *
-     * @param provision external resource
-     * @param mapItem mapping item for the given attribute
-     * @param any given any object
-     * @param password clear-text password
-     * @return connObjectKey + prepared attribute
-     */
-    private Pair<String, Attribute> prepareAttr(
-            final Provision provision, final Item mapItem, final Any<?> any, final String password) {
+    @Override
+    public Pair<String, Attribute> prepareAttr(
+            final Provision provision,
+            final Item item,
+            final Any<?> any,
+            final String password) {
 
         IntAttrName intAttrName;
         try {
-            intAttrName = intAttrNameParser.parse(mapItem.getIntAttrName(), provision.getAnyType().getKind());
+            intAttrName = intAttrNameParser.parse(item.getIntAttrName(), provision.getAnyType().getKind());
         } catch (ParseException e) {
-            LOG.error("Invalid intAttrName '{}' specified, ignoring", mapItem.getIntAttrName(), e);
+            LOG.error("Invalid intAttrName '{}' specified, ignoring", item.getIntAttrName(), e);
             return null;
         }
 
@@ -345,13 +340,13 @@ public class MappingManagerImpl implements MappingManager {
             }
         }
 
-        List<PlainAttrValue> values = getIntValues(provision, mapItem, intAttrName, any);
+        List<PlainAttrValue> values = getIntValues(provision, item, intAttrName, any);
 
         LOG.debug("Define mapping for: "
-                + "\n* ExtAttrName " + mapItem.getExtAttrName()
-                + "\n* is connObjectKey " + mapItem.isConnObjectKey()
-                + "\n* is password " + mapItem.isPassword()
-                + "\n* mandatory condition " + mapItem.getMandatoryCondition()
+                + "\n* ExtAttrName " + item.getExtAttrName()
+                + "\n* is connObjectKey " + item.isConnObjectKey()
+                + "\n* is password " + item.isPassword()
+                + "\n* mandatory condition " + item.getMandatoryCondition()
                 + "\n* Schema " + intAttrName.getSchemaName()
                 + "\n* ClassType " + schemaType.getType().getName()
                 + "\n* Values " + values);
@@ -370,9 +365,9 @@ public class MappingManagerImpl implements MappingManager {
                 }
             }
 
-            if (mapItem.isConnObjectKey()) {
+            if (item.isConnObjectKey()) {
                 result = Pair.of(objValues.isEmpty() ? null : objValues.iterator().next().toString(), null);
-            } else if (mapItem.isPassword() && any instanceof User) {
+            } else if (item.isPassword() && any instanceof User) {
                 String passwordAttrValue = password;
                 if (StringUtils.isBlank(passwordAttrValue)) {
                     User user = (User) any;
@@ -398,8 +393,8 @@ public class MappingManagerImpl implements MappingManager {
                 }
             } else {
                 result = Pair.of(null, objValues.isEmpty()
-                        ? AttributeBuilder.build(mapItem.getExtAttrName())
-                        : AttributeBuilder.build(mapItem.getExtAttrName(), objValues));
+                        ? AttributeBuilder.build(item.getExtAttrName())
+                        : AttributeBuilder.build(item.getExtAttrName(), objValues));
             }
         }
 
index 2ced472..a06d54a 100644 (file)
@@ -59,6 +59,7 @@ import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
 import org.apache.syncope.core.persistence.api.entity.DerSchema;
+import org.apache.syncope.core.persistence.api.entity.Entity;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.GroupablePlainAttr;
 import org.apache.syncope.core.persistence.api.entity.GroupableRelatable;
@@ -593,9 +594,7 @@ abstract class AbstractAnyDataBinder {
             anyTO.getVirAttrs().add(attrTOBuilder.build());
         });
 
-        resources.forEach(resource -> {
-            anyTO.getResources().add(resource.getKey());
-        });
+        anyTO.getResources().addAll(resources.stream().map(Entity::getKey).collect(Collectors.toSet()));
     }
 
     protected RelationshipTO getRelationshipTO(final String relationshipType, final Any<?> otherEnd) {
index e09ab0e..129d0e4 100644 (file)
@@ -423,10 +423,10 @@ public class AnyObjectDataBinderImpl extends AbstractAnyDataBinder implements An
         }
 
         // check if some connObjectKey was changed by the update above
-        Map<String, String> newcCnnObjectKeys = getConnObjectKeys(anyObject, anyUtils);
+        Map<String, String> newConnObjectKeys = getConnObjectKeys(anyObject, anyUtils);
         oldConnObjectKeys.entrySet().stream().
-                filter(entry -> newcCnnObjectKeys.containsKey(entry.getKey())
-                && !entry.getValue().equals(newcCnnObjectKeys.get(entry.getKey()))).
+                filter(entry -> newConnObjectKeys.containsKey(entry.getKey())
+                && !entry.getValue().equals(newConnObjectKeys.get(entry.getKey()))).
                 forEach(entry -> {
                     propByRes.addOldConnObjectKey(entry.getKey(), entry.getValue());
                     propByRes.add(ResourceOperation.UPDATE, entry.getKey());
index 73d653e..91df46f 100644 (file)
@@ -40,6 +40,7 @@ import org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
 import org.apache.syncope.core.provisioning.api.pushpull.PullActions;
 import org.apache.syncope.core.persistence.api.dao.PullCorrelationRule;
+import org.apache.syncope.core.persistence.api.dao.PushCorrelationRule;
 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.serialization.POJOHelper;
@@ -128,6 +129,10 @@ public class ImplementationDataBinderImpl implements ImplementationDataBinder {
                     base = PullCorrelationRule.class;
                     break;
 
+                case PUSH_CORRELATION_RULE:
+                    base = PushCorrelationRule.class;
+                    break;
+
                 case VALIDATOR:
                     base = Validator.class;
                     break;
@@ -152,12 +157,13 @@ public class ImplementationDataBinderImpl implements ImplementationDataBinder {
                 }
             } else if (implementation.getType() == ImplementationType.ACCOUNT_RULE
                     || implementation.getType() == ImplementationType.PASSWORD_RULE
-                    || implementation.getType() == ImplementationType.PULL_CORRELATION_RULE) {
+                    || implementation.getType() == ImplementationType.PULL_CORRELATION_RULE
+                    || implementation.getType() == ImplementationType.PUSH_CORRELATION_RULE) {
 
                 RuleConf rule = POJOHelper.deserialize(implementation.getBody(), RuleConf.class);
                 if (rule == null) {
-                    sce.getElements().
-                            add("Could not deserialize as neither Account, Password nor PullCorrelation RuleConf");
+                    sce.getElements().add("Could not deserialize as neither "
+                            + "Account, Password, Pull nor Push Correlation RuleConf");
                     throw sce;
                 }
             } else {
@@ -188,5 +194,4 @@ public class ImplementationDataBinderImpl implements ImplementationDataBinder {
         BeanUtils.copyProperties(implementation, implementationTO);
         return implementationTO;
     }
-
 }
index a1a400c..53122b5 100644 (file)
@@ -24,6 +24,7 @@ import org.apache.syncope.common.lib.policy.PolicyTO;
 import org.apache.syncope.common.lib.policy.AccountPolicyTO;
 import org.apache.syncope.common.lib.policy.PasswordPolicyTO;
 import org.apache.syncope.common.lib.policy.PullPolicyTO;
+import org.apache.syncope.common.lib.policy.PushPolicyTO;
 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.ImplementationDAO;
@@ -37,9 +38,11 @@ import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.persistence.api.entity.Realm;
-import org.apache.syncope.core.persistence.api.entity.policy.CorrelationRule;
 import org.apache.syncope.core.persistence.api.entity.policy.Policy;
+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.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -138,9 +141,9 @@ public class PolicyDataBinderImpl implements PolicyDataBinder {
                 if (anyType == null) {
                     LOG.debug("Invalid AnyType {} specified, ignoring...", type);
                 } else {
-                    CorrelationRule correlationRule = pullPolicy.getCorrelationRule(anyType).orElse(null);
+                    PullCorrelationRuleEntity correlationRule = pullPolicy.getCorrelationRule(anyType).orElse(null);
                     if (correlationRule == null) {
-                        correlationRule = entityFactory.newEntity(CorrelationRule.class);
+                        correlationRule = entityFactory.newEntity(PullCorrelationRuleEntity.class);
                         correlationRule.setAnyType(anyType);
                         correlationRule.setPullPolicy(pullPolicy);
                         pullPolicy.add(correlationRule);
@@ -148,7 +151,7 @@ public class PolicyDataBinderImpl implements PolicyDataBinder {
 
                     Implementation rule = implementationDAO.find(impl);
                     if (rule == null) {
-                        throw new NotFoundException("Implementation " + type);
+                        throw new NotFoundException("Implementation " + type + " " + impl);
                     }
                     correlationRule.setImplementation(rule);
                 }
@@ -156,6 +159,39 @@ public class PolicyDataBinderImpl implements PolicyDataBinder {
             // remove all rules not contained in the TO
             pullPolicy.getCorrelationRules().removeIf(anyFilter
                     -> !pullPolicyTO.getCorrelationRules().containsKey(anyFilter.getAnyType().getKey()));
+        } else if (policyTO instanceof PushPolicyTO) {
+            if (result == null) {
+                result = (T) entityFactory.newEntity(PushPolicy.class);
+            }
+
+            PushPolicy pushPolicy = PushPolicy.class.cast(result);
+            PushPolicyTO pushPolicyTO = PushPolicyTO.class.cast(policyTO);
+
+            pushPolicy.setConflictResolutionAction(pushPolicyTO.getConflictResolutionAction());
+
+            pushPolicyTO.getCorrelationRules().forEach((type, impl) -> {
+                AnyType anyType = anyTypeDAO.find(type);
+                if (anyType == null) {
+                    LOG.debug("Invalid AnyType {} specified, ignoring...", type);
+                } else {
+                    PushCorrelationRuleEntity correlationRule = pushPolicy.getCorrelationRule(anyType).orElse(null);
+                    if (correlationRule == null) {
+                        correlationRule = entityFactory.newEntity(PushCorrelationRuleEntity.class);
+                        correlationRule.setAnyType(anyType);
+                        correlationRule.setPushPolicy(pushPolicy);
+                        pushPolicy.add(correlationRule);
+                    }
+
+                    Implementation rule = implementationDAO.find(impl);
+                    if (rule == null) {
+                        throw new NotFoundException("Implementation " + type + " " + impl);
+                    }
+                    correlationRule.setImplementation(rule);
+                }
+            });
+            // remove all rules not contained in the TO
+            pushPolicy.getCorrelationRules().removeIf(anyFilter
+                    -> !pushPolicyTO.getCorrelationRules().containsKey(anyFilter.getAnyType().getKey()));
         }
 
         if (result != null) {
@@ -212,6 +248,15 @@ public class PolicyDataBinderImpl implements PolicyDataBinder {
             pullPolicy.getCorrelationRules().forEach(rule -> {
                 pullPolicyTO.getCorrelationRules().put(rule.getAnyType().getKey(), rule.getImplementation().getKey());
             });
+        } else if (policy instanceof PushPolicy) {
+            PushPolicy pushPolicy = PushPolicy.class.cast(policy);
+            PushPolicyTO pushPolicyTO = new PushPolicyTO();
+            policyTO = (T) pushPolicyTO;
+
+            pushPolicyTO.setConflictResolutionAction(((PushPolicy) policy).getConflictResolutionAction());
+            pushPolicy.getCorrelationRules().forEach(rule -> {
+                pushPolicyTO.getCorrelationRules().put(rule.getAnyType().getKey(), rule.getImplementation().getKey());
+            });
         }
 
         if (policyTO != null) {
index fedc288..de0c9ac 100644 (file)
@@ -65,6 +65,7 @@ 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.PullPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResourceHistoryConf;
 import org.apache.syncope.core.persistence.api.entity.resource.Item;
 import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
@@ -381,6 +382,9 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
         resource.setPullPolicy(resourceTO.getPullPolicy() == null
                 ? null : (PullPolicy) policyDAO.find(resourceTO.getPullPolicy()));
 
+        resource.setPushPolicy(resourceTO.getPushPolicy() == null
+                ? null : (PushPolicy) policyDAO.find(resourceTO.getPushPolicy()));
+
         resource.setConfOverride(new HashSet<>(resourceTO.getConfOverride()));
 
         resource.setOverrideCapabilities(resourceTO.isOverrideCapabilities());
@@ -675,6 +679,9 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
         resourceTO.setPullPolicy(resource.getPullPolicy() == null
                 ? null : resource.getPullPolicy().getKey());
 
+        resourceTO.setPushPolicy(resource.getPushPolicy() == null
+                ? null : resource.getPushPolicy().getKey());
+
         resourceTO.getConfOverride().addAll(resource.getConfOverride());
         Collections.sort(resourceTO.getConfOverride());
 
index fa9cb9f..aaec195 100644 (file)
@@ -525,10 +525,10 @@ public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDat
         }
 
         // check if some connObjectKey was changed by the update above
-        Map<String, String> newcCnnObjectKeys = getConnObjectKeys(user, anyUtils);
+        Map<String, String> newConnObjectKeys = getConnObjectKeys(user, anyUtils);
         oldConnObjectKeys.entrySet().stream().
-                filter(entry -> newcCnnObjectKeys.containsKey(entry.getKey())
-                && !entry.getValue().equals(newcCnnObjectKeys.get(entry.getKey()))).
+                filter(entry -> newConnObjectKeys.containsKey(entry.getKey())
+                && !entry.getValue().equals(newConnObjectKeys.get(entry.getKey()))).
                 forEach(entry -> {
                     propByRes.addOldConnObjectKey(entry.getKey(), entry.getValue());
                     propByRes.add(ResourceOperation.UPDATE, entry.getKey());
index 684c021..bf770d1 100644 (file)
@@ -33,7 +33,7 @@ public class SyncopeJexlFunctions {
     /**
      * Converts realm's full path into the equivalent DN.
      *
-     * Example: {@code}/a/b/c{@code} becomes {@code}ou=c,ou=b,ou=a{@code}.
+     * Example: {@code /a/b/c} becomes {@code ou=c,ou=b,ou=a}.
      *
      * @param fullPath realm's full path
      * @param attr attribute name for DN
@@ -46,8 +46,8 @@ public class SyncopeJexlFunctions {
     /**
      * Converts realm's full path into the equivalent DN.
      *
-     * Example: {@code}/a/b/c{@code} becomes {@code},ou=c,ou=b,ou=a{@code}, when {@code}prefix{@code} is
-     * {@code}&quot;,&quot;{@code}
+     * Example: {@code /a/b/c} becomes {@code ,ou=c,ou=b,ou=a}, when {@code prefix} is
+     * {@code &quot;,&quot;}
      *
      * @param fullPath realm's full path
      * @param attr attribute name for DN
index e51690e..6de2eec 100644 (file)
@@ -501,7 +501,7 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
 
                         AnyPatch anyPatch = null;
                         if (unlink) {
-                            anyPatch = newPatch(key);
+                            anyPatch = getAnyUtils().newAnyPatch(key);
                             anyPatch.getResources().add(new StringPatchItem.Builder().
                                     operation(PatchOperation.DELETE).
                                     value(profile.getTask().getResource().getKey()).build());
@@ -604,7 +604,7 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
                             }
                         }
 
-                        AnyPatch anyPatch = newPatch(before.getKey());
+                        AnyPatch anyPatch = getAnyUtils().newAnyPatch(before.getKey());
                         anyPatch.getResources().add(new StringPatchItem.Builder().
                                 operation(unlink ? PatchOperation.DELETE : PatchOperation.ADD_REPLACE).
                                 value(profile.getTask().getResource().getKey()).build());
@@ -804,9 +804,9 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
                     processed.getUid().getUidValue(), processed.getObject().getObjectClass(), anyKeys);
 
             if (anyKeys.size() > 1) {
-                switch (profile.getResAct()) {
+                switch (profile.getConflictResolutionAction()) {
                     case IGNORE:
-                        throw new IllegalStateException("More than one match " + anyKeys);
+                        throw new IllegalStateException("More than one match: " + anyKeys);
 
                     case FIRSTMATCH:
                         anyKeys = anyKeys.subList(0, 1);
@@ -915,8 +915,10 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
             this.latestResult = result;
         }
 
+        AnyUtils anyUtils = getAnyUtils();
+
         notificationManager.createTasks(AuditElements.EventCategoryType.PULL,
-                getAnyUtils().anyTypeKind().name().toLowerCase(),
+                anyUtils.anyTypeKind().name().toLowerCase(),
                 profile.getTask().getResource().getKey(),
                 event,
                 result,
@@ -926,7 +928,7 @@ public abstract class AbstractPullResultHandler extends AbstractSyncopeResultHan
                 furtherInput);
 
         auditManager.audit(AuditElements.EventCategoryType.PULL,
-                getAnyUtils().anyTypeKind().name().toLowerCase(),
+                anyUtils.anyTypeKind().name().toLowerCase(),
                 profile.getTask().getResource().getKey(),
                 event,
                 result,
index 7a98f90..06f6da9 100644 (file)
 package org.apache.syncope.core.provisioning.java.pushpull;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
+import java.util.stream.Collectors;
 import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.apache.syncope.common.lib.patch.AnyPatch;
 import org.apache.syncope.common.lib.patch.StringPatchItem;
@@ -42,24 +40,17 @@ import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningReport;
 import org.apache.syncope.core.provisioning.api.pushpull.PushActions;
 import org.apache.syncope.core.persistence.api.entity.Any;
-import org.apache.syncope.core.persistence.api.entity.AnyUtils;
-import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
-import org.apache.syncope.core.persistence.api.entity.resource.Item;
-import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
+import org.apache.syncope.core.persistence.api.entity.Entity;
 import org.apache.syncope.core.persistence.api.entity.resource.Provision;
 import org.apache.syncope.core.provisioning.api.AuditManager;
 import org.apache.syncope.core.provisioning.api.MappingManager;
-import org.apache.syncope.core.provisioning.api.TimeoutException;
 import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent;
 import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
 import org.apache.syncope.core.provisioning.api.pushpull.IgnoreProvisionException;
 import org.apache.syncope.core.provisioning.api.pushpull.SyncopePushResultHandler;
 import org.apache.syncope.core.provisioning.java.job.AfterHandlingJob;
-import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
-import org.identityconnectors.framework.common.objects.AttributeBuilder;
 import org.identityconnectors.framework.common.objects.ConnectorObject;
-import org.identityconnectors.framework.common.objects.ObjectClass;
 import org.quartz.JobExecutionException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.quartz.SchedulerFactoryBean;
@@ -69,6 +60,9 @@ import org.springframework.transaction.annotation.Transactional;
 public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHandler<PushTask, PushActions>
         implements SyncopePushResultHandler {
 
+    @Autowired
+    protected PushUtils pushUtils;
+
     /**
      * Notification Manager.
      */
@@ -96,25 +90,17 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
         }
     }
 
-    protected void update(final Any<?> any, final ProvisioningReport result) {
-        boolean changepwd;
-        Collection<String> resourceKeys;
-        if (any instanceof User) {
-            changepwd = true;
-            resourceKeys = userDAO.findAllResourceKeys(any.getKey());
-        } else if (any instanceof AnyObject) {
-            changepwd = false;
-            resourceKeys = anyObjectDAO.findAllResourceKeys(any.getKey());
-        } else {
-            changepwd = false;
-            resourceKeys = groupDAO.findAllResourceKeys(any.getKey());
-        }
+    protected void update(final Any<?> any, final ConnectorObject beforeObj, final ProvisioningReport result) {
+        boolean changepwd = any instanceof User;
+        List<String> ownedResources = getAnyUtils().getAllResources(any).stream().
+                map(Entity::getKey).collect(Collectors.toList());
 
-        List<String> noPropResources = new ArrayList<>(resourceKeys);
+        List<String> noPropResources = new ArrayList<>(ownedResources);
         noPropResources.remove(profile.getTask().getResource().getKey());
 
         PropagationByResource propByRes = new PropagationByResource();
-        propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
+        propByRes.add(ResourceOperation.UPDATE, profile.getTask().getResource().getKey());
+        propByRes.addOldConnObjectKey(profile.getTask().getResource().getKey(), beforeObj.getUid().getUidValue());
 
         PropagationReporter reporter = taskExecutor.execute(propagationManager.getUpdateTasks(
                 any.getType().getKind(),
@@ -128,16 +114,20 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
         reportPropagation(result, reporter);
     }
 
-    protected void deprovision(final Any<?> any, final ProvisioningReport result) {
+    protected void deprovision(final Any<?> any, final ConnectorObject beforeObj, final ProvisioningReport result) {
         AnyTO before = getAnyTO(any.getKey());
 
         List<String> noPropResources = new ArrayList<>(before.getResources());
         noPropResources.remove(profile.getTask().getResource().getKey());
 
+        PropagationByResource propByRes = new PropagationByResource();
+        propByRes.add(ResourceOperation.DELETE, profile.getTask().getResource().getKey());
+        propByRes.addOldConnObjectKey(profile.getTask().getResource().getKey(), beforeObj.getUid().getUidValue());
+
         PropagationReporter reporter = taskExecutor.execute(propagationManager.getDeleteTasks(
                 any.getType().getKind(),
                 any.getKey(),
-                null,
+                propByRes,
                 noPropResources),
                 false);
         reportPropagation(result, reporter);
@@ -163,7 +153,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
     }
 
     protected void link(final Any<?> any, final boolean unlink, final ProvisioningReport result) {
-        AnyPatch patch = newPatch(any.getKey());
+        AnyPatch patch = getAnyUtils().newAnyPatch(any.getKey());
         patch.getResources().add(new StringPatchItem.Builder().
                 operation(unlink ? PatchOperation.DELETE : PatchOperation.ADD_REPLACE).
                 value(profile.getTask().getResource().getKey()).build());
@@ -173,19 +163,19 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
         result.setStatus(ProvisioningReport.Status.SUCCESS);
     }
 
-    protected void unassign(final Any<?> any, final ProvisioningReport result) {
-        AnyPatch patch = newPatch(any.getKey());
+    protected void unassign(final Any<?> any, final ConnectorObject beforeObj, final ProvisioningReport result) {
+        AnyPatch patch = getAnyUtils().newAnyPatch(any.getKey());
         patch.getResources().add(new StringPatchItem.Builder().
                 operation(PatchOperation.DELETE).
                 value(profile.getTask().getResource().getKey()).build());
 
         update(patch);
 
-        deprovision(any, result);
+        deprovision(any, beforeObj, result);
     }
 
     protected void assign(final Any<?> any, final Boolean enabled, final ProvisioningReport result) {
-        AnyPatch patch = newPatch(any.getKey());
+        AnyPatch patch = getAnyUtils().newAnyPatch(any.getKey());
         patch.getResources().add(new StringPatchItem.Builder().
                 operation(PatchOperation.ADD_REPLACE).
                 value(profile.getTask().getResource().getKey()).build());
@@ -195,37 +185,20 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
         provision(any, enabled, result);
     }
 
-    protected ConnectorObject getRemoteObject(
-            final ObjectClass objectClass,
-            final String connObjectKey,
-            final String connObjectKeyValue,
-            final boolean ignoreCaseMatch,
-            final Iterator<? extends Item> iterator) {
-
-        ConnectorObject obj = null;
-        try {
-            obj = profile.getConnector().getObject(
-                    objectClass,
-                    AttributeBuilder.build(connObjectKey, connObjectKeyValue),
-                    ignoreCaseMatch,
-                    MappingUtils.buildOperationOptions(iterator));
-        } catch (TimeoutException toe) {
-            LOG.debug("Request timeout", toe);
-            throw toe;
-        } catch (RuntimeException ignore) {
-            LOG.debug("While resolving {}", connObjectKeyValue, ignore);
-        }
-
-        return obj;
-    }
-
     @Transactional(propagation = Propagation.REQUIRES_NEW)
     @Override
     public boolean handle(final String anyKey) {
         Any<?> any = null;
         try {
-            any = getAny(anyKey);
-            doHandle(any);
+            any = getAnyUtils().dao().authFind(anyKey);
+
+            Provision provision = profile.getTask().getResource().getProvision(any.getType()).orElse(null);
+            if (provision == null) {
+                throw new JobExecutionException("No provision found on " + profile.getTask().getResource() + " for "
+                        + any.getType().getKey());
+            }
+
+            doHandle(any, provision);
             return true;
         } catch (IgnoreProvisionException e) {
             ProvisioningReport result = profile.getResults().stream().
@@ -251,9 +224,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
         }
     }
 
-    protected void doHandle(final Any<?> any) throws JobExecutionException {
-        AnyUtils anyUtils = anyUtilsFactory.getInstance(any);
-
+    protected void doHandle(final Any<?> any, final Provision provision) throws JobExecutionException {
         ProvisioningReport result = new ProvisioningReport();
         profile.getResults().add(result);
 
@@ -261,32 +232,39 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
         result.setAnyType(any.getType().getKey());
         result.setName(getName(any));
 
-        Boolean enabled = any instanceof User && profile.getTask().isSyncStatus()
-                ? ((User) any).isSuspended() ? Boolean.FALSE : Boolean.TRUE
-                : null;
-
         LOG.debug("Propagating {} with key {} towards {}",
-                anyUtils.anyTypeKind(), any.getKey(), profile.getTask().getResource());
+                any.getType().getKind(), any.getKey(), profile.getTask().getResource());
+
+        // Try to read remote object BEFORE any actual operation
+        List<ConnectorObject> connObjs = pushUtils.match(profile.getConnector(), any, provision);
+        LOG.debug("Match(es) found for {} as {}: {}", any, provision.getObjectClass(), connObjs);
+
+        if (connObjs.size() > 1) {
+            switch (profile.getConflictResolutionAction()) {
+                case IGNORE:
+                    throw new IllegalStateException("More than one match: " + connObjs);
+
+                case FIRSTMATCH:
+                    connObjs = connObjs.subList(0, 1);
+                    break;
+
+                case LASTMATCH:
+                    connObjs = connObjs.subList(connObjs.size() - 1, connObjs.size());
+                    break;
+
+                default:
+            }
+        }
+        ConnectorObject beforeObj = connObjs.isEmpty() ? null : connObjs.get(0);
 
         Object output = null;
         Result resultStatus = null;
 
-        // Try to read remote object BEFORE any actual operation
-        Optional<? extends Provision> provision = profile.getTask().getResource().getProvision(any.getType());
-        Optional<MappingItem> connObjectKey = MappingUtils.getConnObjectKeyItem(provision.get());
-        Optional<String> connObjecKeyValue = mappingManager.getConnObjectKeyValue(any, provision.get());
-
-        ConnectorObject beforeObj = null;
-        if (connObjectKey.isPresent() && connObjecKeyValue.isPresent()) {
-            beforeObj = getRemoteObject(
-                    provision.get().getObjectClass(),
-                    connObjectKey.get().getExtAttrName(),
-                    connObjecKeyValue.get(),
-                    provision.get().isIgnoreCaseMatch(),
-                    provision.get().getMapping().getItems().iterator());
-        } else {
-            LOG.debug("ConnObjectKeyItem {} or its value {} are null", connObjectKey, connObjecKeyValue);
-        }
+        Boolean enabled = any instanceof User && profile.getTask().isSyncStatus()
+                ? ((User) any).isSuspended()
+                ? Boolean.FALSE
+                : Boolean.TRUE
+                : null;
 
         Boolean status = profile.getTask().isSyncStatus() ? enabled : null;
 
@@ -376,7 +354,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
                                 LOG.debug("PushTask not configured for update");
                                 result.setStatus(ProvisioningReport.Status.IGNORE);
                             } else {
-                                update(any, result);
+                                update(any, beforeObj, result);
                             }
                             break;
 
@@ -389,7 +367,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
                                 LOG.debug("PushTask not configured for delete");
                                 result.setStatus(ProvisioningReport.Status.IGNORE);
                             } else {
-                                deprovision(any, result);
+                                deprovision(any, beforeObj, result);
                             }
                             break;
 
@@ -402,7 +380,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
                                 LOG.debug("PushTask not configured for delete");
                                 result.setStatus(ProvisioningReport.Status.IGNORE);
                             } else {
-                                unassign(any, result);
+                                unassign(any, beforeObj, result);
                             }
                             break;
 
@@ -451,14 +429,7 @@ public abstract class AbstractPushResultHandler extends AbstractSyncopeResultHan
                     result.setStatus(ProvisioningReport.Status.SUCCESS);
                 }
                 resultStatus = AuditElements.Result.SUCCESS;
-                if (connObjectKey.isPresent() && connObjecKeyValue.isPresent()) {
-                    output = getRemoteObject(
-                            provision.get().getObjectClass(),
-                            connObjectKey.get().getExtAttrName(),
-                            connObjecKeyValue.get(),
-                            provision.get().isIgnoreCaseMatch(),
-                            provision.get().getMapping().getItems().iterator());
-                }
+                output = pushUtils.findByConnObjectKey(profile.getConnector(), any, provision);
             } catch (IgnoreProvisionException e) {
                 throw e;
             } catch (Exception e) {
index 53c4258..4e0b229 100644 (file)
@@ -20,8 +20,6 @@ package org.apache.syncope.core.provisioning.java.pushpull;
 
 import org.apache.syncope.common.lib.patch.AnyPatch;
 import org.apache.syncope.common.lib.to.AnyTO;
-import org.apache.syncope.core.persistence.api.dao.GroupDAO;
-import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask;
 import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
 import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
@@ -29,8 +27,6 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
 import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
 import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
 import org.apache.syncope.core.provisioning.api.pushpull.SyncopeResultHandler;
-import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
-import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
@@ -48,15 +44,6 @@ public abstract class AbstractSyncopeResultHandler<T extends ProvisioningTask, A
 
     protected static final Logger LOG = LoggerFactory.getLogger(SyncopeResultHandler.class);
 
-    @Autowired
-    protected AnyObjectDAO anyObjectDAO;
-
-    @Autowired
-    protected UserDAO userDAO;
-
-    @Autowired
-    protected GroupDAO groupDAO;
-
     /**
      * Propagation manager.
      */
@@ -105,10 +92,6 @@ public abstract class AbstractSyncopeResultHandler<T extends ProvisioningTask, A
 
     protected abstract AnyTO getAnyTO(String key);
 
-    protected abstract Any<?> getAny(String key);
-
-    protected abstract AnyPatch newPatch(String key);
-
     protected abstract WorkflowResult<? extends AnyPatch> update(AnyPatch patch);
 
     @Override
index 8d71a2d..bb6e61e 100644 (file)
@@ -28,7 +28,6 @@ import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.to.PropagationStatus;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager;
 import org.apache.syncope.core.provisioning.api.ProvisioningManager;
@@ -59,28 +58,11 @@ public class DefaultAnyObjectPullResultHandler extends AbstractPullResultHandler
     }
 
     @Override
-    protected Any<?> getAny(final String key) {
-        try {
-            return anyObjectDAO.authFind(key);
-        } catch (Exception e) {
-            LOG.warn("Error retrieving anyObject {}", key, e);
-            return null;
-        }
-    }
-
-    @Override
     protected AnyTO getAnyTO(final String key) {
         return anyObjectDataBinder.getAnyObjectTO(key);
     }
 
     @Override
-    protected AnyPatch newPatch(final String key) {
-        AnyObjectPatch patch = new AnyObjectPatch();
-        patch.setKey(key);
-        return patch;
-    }
-
-    @Override
     protected WorkflowResult<? extends AnyPatch> update(final AnyPatch patch) {
         return awfAdapter.update((AnyObjectPatch) patch);
     }
index 3c401ae..a2b7e35 100644 (file)
  */
 package org.apache.syncope.core.provisioning.java.pushpull;
 
-import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.patch.AnyObjectPatch;
 import org.apache.syncope.common.lib.patch.AnyPatch;
 import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 import org.apache.syncope.core.provisioning.api.WorkflowResult;
 import org.apache.syncope.core.provisioning.api.pushpull.AnyObjectPushResultHandler;
 
@@ -37,17 +37,7 @@ public class DefaultAnyObjectPushResultHandler extends AbstractPushResultHandler
 
     @Override
     protected String getName(final Any<?> any) {
-        return StringUtils.EMPTY;
-    }
-
-    @Override
-    protected Any<?> getAny(final String key) {
-        try {
-            return anyObjectDAO.authFind(key);
-        } catch (Exception e) {
-            LOG.warn("Error retrieving anyObject {}", key, e);
-            return null;
-        }
+        return AnyObject.class.cast(any).getName();
     }
 
     @Override
@@ -56,15 +46,7 @@ public class DefaultAnyObjectPushResultHandler extends AbstractPushResultHandler
     }
 
     @Override
-    protected AnyPatch newPatch(final String key) {
-        AnyObjectPatch patch = new AnyObjectPatch();
-        patch.setKey(key);
-        return patch;
-    }
-
-    @Override
     protected WorkflowResult<? extends AnyObjectPatch> update(final AnyPatch patch) {
         return awfAdapter.update((AnyObjectPatch) patch);
     }
-
 }
index 451659b..e377123 100644 (file)
@@ -31,7 +31,6 @@ import org.apache.syncope.common.lib.to.PropagationStatus;
 import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.PatchOperation;
-import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 import org.apache.syncope.core.provisioning.api.GroupProvisioningManager;
 import org.apache.syncope.core.provisioning.api.ProvisioningManager;
@@ -69,28 +68,11 @@ public class DefaultGroupPullResultHandler extends AbstractPullResultHandler imp
     }
 
     @Override
-    protected Any<?> getAny(final String key) {
-        try {
-            return groupDAO.authFind(key);
-        } catch (Exception e) {
-            LOG.warn("Error retrieving group {}", key, e);
-            return null;
-        }
-    }
-
-    @Override
     protected AnyTO getAnyTO(final String key) {
         return groupDataBinder.getGroupTO(key);
     }
 
     @Override
-    protected AnyPatch newPatch(final String key) {
-        GroupPatch patch = new GroupPatch();
-        patch.setKey(key);
-        return patch;
-    }
-
-    @Override
     protected WorkflowResult<? extends AnyPatch> update(final AnyPatch patch) {
         return gwfAdapter.update((GroupPatch) patch);
     }
index 993f747..3c5f59b 100644 (file)
@@ -41,30 +41,12 @@ public class DefaultGroupPushResultHandler extends AbstractPushResultHandler imp
     }
 
     @Override
-    protected Any<?> getAny(final String key) {
-        try {
-            return groupDAO.authFind(key);
-        } catch (Exception e) {
-            LOG.warn("Error retrieving group {}", key, e);
-            return null;
-        }
-    }
-
-    @Override
     protected AnyTO getAnyTO(final String key) {
         return groupDataBinder.getGroupTO(key);
     }
 
     @Override
-    protected AnyPatch newPatch(final String key) {
-        GroupPatch patch = new GroupPatch();
-        patch.setKey(key);
-        return patch;
-    }
-
-    @Override
     protected WorkflowResult<? extends AnyPatch> update(final AnyPatch patch) {
         return gwfAdapter.update((GroupPatch) patch);
     }
-
 }
index b1dcf3d..be0caab 100644 (file)
@@ -684,7 +684,7 @@ public class DefaultRealmPullResultHandler
                 processed.getUid().getUidValue(), processed.getObject().getObjectClass(), keys);
 
         if (keys.size() > 1) {
-            switch (profile.getResAct()) {
+            switch (profile.getConflictResolutionAction()) {
                 case IGNORE:
                     throw new IllegalStateException("More than one match " + keys);
 
index 83a5401..e600c93 100644 (file)
@@ -28,7 +28,6 @@ import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.to.PropagationStatus;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
 import org.apache.syncope.core.provisioning.api.ProvisioningManager;
 import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
@@ -59,28 +58,11 @@ public class DefaultUserPullResultHandler extends AbstractPullResultHandler impl
     }
 
     @Override
-    protected Any<?> getAny(final String key) {
-        try {
-            return userDAO.authFind(key);
-        } catch (Exception e) {
-            LOG.warn("Error retrieving user {}", key, e);
-            return null;
-        }
-    }
-
-    @Override
     protected AnyTO getAnyTO(final String key) {
         return userDataBinder.getUserTO(key);
     }
 
     @Override
-    protected AnyPatch newPatch(final String key) {
-        UserPatch patch = new UserPatch();
-        patch.setKey(key);
-        return patch;
-    }
-
-    @Override
     protected WorkflowResult<? extends AnyPatch> update(final AnyPatch patch) {
         WorkflowResult<Pair<UserPatch, Boolean>> update = uwfAdapter.update((UserPatch) patch);
         return new WorkflowResult<>(update.getResult().getLeft(), update.getPropByRes(), update.getPerformedTasks());
index 70f3a00..05dbb54 100644 (file)
@@ -69,31 +69,13 @@ public class DefaultUserPushResultHandler extends AbstractPushResultHandler impl
     }
 
     @Override
-    protected Any<?> getAny(final String key) {
-        try {
-            return userDAO.authFind(key);
-        } catch (Exception e) {
-            LOG.warn("Error retrieving user {}", key, e);
-            return null;
-        }
-    }
-
-    @Override
     protected AnyTO getAnyTO(final String key) {
         return userDataBinder.getUserTO(key);
     }
 
     @Override
-    protected AnyPatch newPatch(final String key) {
-        UserPatch patch = new UserPatch();
-        patch.setKey(key);
-        return patch;
-    }
-
-    @Override
     protected WorkflowResult<? extends AnyPatch> update(final AnyPatch patch) {
         WorkflowResult<Pair<UserPatch, Boolean>> update = uwfAdapter.update((UserPatch) patch);
         return new WorkflowResult<>(update.getResult().getLeft(), update.getPropByRes(), update.getPerformedTasks());
     }
-
 }
index 0bcf115..f69b4c9 100644 (file)
@@ -212,7 +212,7 @@ public class PullJobDelegate extends AbstractProvisioningJobDelegate<PullTask> i
         profile = new ProvisioningProfile<>(connector, pullTask);
         profile.getActions().addAll(actions);
         profile.setDryRun(dryRun);
-        profile.setResAct(pullTask.getResource().getPullPolicy() == null
+        profile.setConflictResolutionAction(pullTask.getResource().getPullPolicy() == null
                 ? ConflictResolutionAction.IGNORE
                 : pullTask.getResource().getPullPolicy().getConflictResolutionAction());
 
index 0d61f1b..84b3661 100644 (file)
@@ -30,8 +30,12 @@ import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.PullCorrelationRule;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
+import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
+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.AnyUtils;
@@ -42,7 +46,7 @@ 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.anyobject.AnyObject;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
-import org.apache.syncope.core.persistence.api.entity.policy.CorrelationRule;
+import org.apache.syncope.core.persistence.api.entity.policy.PullCorrelationRuleEntity;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
 import org.apache.syncope.core.persistence.api.entity.resource.OrgUnit;
@@ -51,26 +55,22 @@ import org.apache.syncope.core.persistence.api.entity.resource.Provision;
 import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.provisioning.api.Connector;
-import org.apache.syncope.core.provisioning.java.IntAttrNameParser;
 import org.apache.syncope.core.provisioning.api.IntAttrName;
+import org.apache.syncope.core.provisioning.api.data.ItemTransformer;
+import org.apache.syncope.core.provisioning.java.IntAttrNameParser;
+import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
+import org.apache.syncope.core.spring.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.Name;
 import org.identityconnectors.framework.common.objects.OperationalAttributes;
+import org.identityconnectors.framework.common.objects.filter.FilterBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
-import org.apache.syncope.core.persistence.api.dao.PullCorrelationRule;
-import org.apache.syncope.core.persistence.api.dao.search.AnyCond;
-import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
-import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
-import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
-import org.apache.syncope.core.provisioning.api.data.ItemTransformer;
-import org.apache.syncope.core.spring.ImplementationManager;
-import org.identityconnectors.framework.common.objects.filter.FilterBuilder;
 
 @Transactional(readOnly = true)
 @Component
@@ -319,7 +319,7 @@ public class PullUtils {
             final Provision provision,
             final AnyUtils anyUtils) {
 
-        Optional<? extends CorrelationRule> correlationRule = provision.getResource().getPullPolicy() == null
+        Optional<? extends PullCorrelationRuleEntity> correlationRule = provision.getResource().getPullPolicy() == null
                 ? Optional.empty()
                 : provision.getResource().getPullPolicy().getCorrelationRule(provision.getAnyType());
 
index 2207f42..9765edb 100644 (file)
@@ -27,6 +27,7 @@ import java.util.Optional;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.MutablePair;
 import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.types.ConflictResolutionAction;
 import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
 import org.apache.syncope.core.spring.ApplicationContextProvider;
 import org.apache.syncope.core.persistence.api.dao.AnyDAO;
@@ -165,7 +166,9 @@ public class PushJobDelegate extends AbstractProvisioningJobDelegate<PushTask> {
         profile = new ProvisioningProfile<>(connector, pushTask);
         profile.getActions().addAll(actions);
         profile.setDryRun(dryRun);
-        profile.setResAct(null);
+        profile.setConflictResolutionAction(pushTask.getResource().getPushPolicy() == null
+                ? ConflictResolutionAction.IGNORE
+                : pushTask.getResource().getPushPolicy().getConflictResolutionAction());
 
         if (!profile.isDryRun()) {
             for (PushActions action : actions) {
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/PushUtils.java
new file mode 100644 (file)
index 0000000..7262a0a
--- /dev/null
@@ -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.provisioning.java.pushpull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import org.apache.syncope.core.persistence.api.dao.PushCorrelationRule;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.policy.PushCorrelationRuleEntity;
+import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
+import org.apache.syncope.core.persistence.api.entity.resource.Provision;
+import org.apache.syncope.core.provisioning.api.Connector;
+import org.apache.syncope.core.provisioning.api.MappingManager;
+import org.apache.syncope.core.provisioning.api.TimeoutException;
+import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
+import org.apache.syncope.core.spring.ImplementationManager;
+import org.identityconnectors.framework.common.objects.AttributeBuilder;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Transactional(readOnly = true)
+@Component
+public class PushUtils {
+
+    private static final Logger LOG = LoggerFactory.getLogger(PushUtils.class);
+
+    @Autowired
+    private MappingManager mappingManager;
+
+    public List<ConnectorObject> match(
+            final Connector connector,
+            final Any<?> any,
+            final Provision provision) {
+
+        Optional<? extends PushCorrelationRuleEntity> correlationRule = provision.getResource().getPushPolicy() == null
+                ? Optional.empty()
+                : provision.getResource().getPushPolicy().getCorrelationRule(provision.getAnyType());
+
+        Optional<PushCorrelationRule> rule = Optional.empty();
+        if (correlationRule.isPresent()) {
+            try {
+                rule = ImplementationManager.buildPushCorrelationRule(correlationRule.get().getImplementation());
+            } catch (Exception e) {
+                LOG.error("While building {}", correlationRule.get().getImplementation(), e);
+            }
+        }
+
+        try {
+            return rule.isPresent()
+                    ? findByCorrelationRule(connector, any, provision, rule.get())
+                    : findByConnObjectKey(connector, any, provision);
+        } catch (RuntimeException e) {
+            LOG.error("Could not match {} with any existing {}", any, provision.getObjectClass(), e);
+            return Collections.<ConnectorObject>emptyList();
+        }
+    }
+
+    private List<ConnectorObject> findByCorrelationRule(
+            final Connector connector,
+            final Any<?> any,
+            final Provision provision,
+            final PushCorrelationRule rule) {
+
+        List<ConnectorObject> objs = new ArrayList<>();
+
+        try {
+            connector.search(
+                    provision.getObjectClass(),
+                    rule.getFilter(any, provision),
+                    obj -> {
+                        objs.add(obj);
+                        return true;
+                    }, MappingUtils.buildOperationOptions(provision.getMapping().getItems().iterator()));
+        } catch (TimeoutException toe) {
+            LOG.debug("Request timeout", toe);
+            throw toe;
+        } catch (RuntimeException ignore) {
+            LOG.debug("Unexpected exception", ignore);
+        }
+
+        return objs;
+    }
+
+    public List<ConnectorObject> findByConnObjectKey(
+            final Connector connector,
+            final Any<?> any,
+            final Provision provision) {
+
+        Optional<MappingItem> connObjectKey = MappingUtils.getConnObjectKeyItem(provision);
+        Optional<String> connObjectKeyValue = mappingManager.getConnObjectKeyValue(any, provision);
+
+        ConnectorObject obj = null;
+        if (connObjectKey.isPresent() && connObjectKeyValue.isPresent()) {
+            try {
+                obj = connector.getObject(
+                        provision.getObjectClass(),
+                        AttributeBuilder.build(connObjectKey.get().getExtAttrName(), connObjectKeyValue.get()),
+                        provision.isIgnoreCaseMatch(),
+                        MappingUtils.buildOperationOptions(provision.getMapping().getItems().iterator()));
+            } catch (TimeoutException toe) {
+                LOG.debug("Request timeout", toe);
+                throw toe;
+            } catch (RuntimeException ignore) {
+                LOG.debug("While resolving {}", connObjectKeyValue.get(), ignore);
+            }
+        }
+
+        return obj == null ? Collections.emptyList() : Collections.singletonList(obj);
+    }
+}
index d0d2b21..f67f750 100644 (file)
@@ -135,7 +135,7 @@ public class SinglePullJobDelegate extends PullJobDelegate implements SyncopeSin
 
             profile = new ProvisioningProfile<>(connector, pullTask);
             profile.setDryRun(false);
-            profile.setResAct(ConflictResolutionAction.FIRSTMATCH);
+            profile.setConflictResolutionAction(ConflictResolutionAction.FIRSTMATCH);
             profile.getActions().addAll(actions);
 
             for (PullActions action : actions) {
index 8f8a3d1..08ff9c7 100644 (file)
@@ -22,6 +22,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import org.apache.syncope.common.lib.to.PushTaskTO;
+import org.apache.syncope.common.lib.types.ConflictResolutionAction;
 import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.common.lib.types.MatchingRule;
 import org.apache.syncope.common.lib.types.UnmatchingRule;
@@ -84,7 +85,7 @@ public class SinglePushJobDelegate extends PushJobDelegate implements SyncopeSin
 
             profile = new ProvisioningProfile<>(connector, pushTask);
             profile.getActions().addAll(actions);
-            profile.setResAct(null);
+            profile.setConflictResolutionAction(ConflictResolutionAction.FIRSTMATCH);
 
             for (PushActions action : actions) {
                 action.beforeAll(profile);
index 63e44cb..d02f458 100644 (file)
@@ -21,6 +21,7 @@ package org.apache.syncope.core.provisioning.java.utils;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.AnyOperations;
 import org.apache.syncope.common.lib.patch.AnyPatch;
@@ -125,7 +126,7 @@ public class ConnObjectUtils {
         final ConnObjectTO connObjectTO = new ConnObjectTO();
 
         if (attrs != null) {
-            attrs.stream().map(attr -> {
+            connObjectTO.getAttrs().addAll(attrs.stream().map(attr -> {
                 AttrTO attrTO = new AttrTO();
                 attrTO.setSchema(attr.getName());
                 if (attr.getValue() != null) {
@@ -134,15 +135,13 @@ public class ConnObjectUtils {
                             attrTO.getValues().add(getPassword(value));
                         } else if (value instanceof byte[]) {
                             attrTO.getValues().add(Base64.encode((byte[]) value));
-                        } else {
+                        } else if (value != null) {
                             attrTO.getValues().add(value.toString());
                         }
                     });
                 }
                 return attrTO;
-            }).forEach(attrTO -> {
-                connObjectTO.getAttrs().add(attrTO);
-            });
+            }).collect(Collectors.toList()));
         }
 
         return connObjectTO;
index 51ac7ad..2bdf518 100644 (file)
@@ -23,16 +23,19 @@ import java.util.Set;
 import org.apache.syncope.common.lib.policy.AccountRuleConf;
 import org.apache.syncope.common.lib.policy.PasswordRuleConf;
 import org.apache.syncope.common.lib.policy.PullCorrelationRuleConf;
+import org.apache.syncope.common.lib.policy.PushCorrelationRuleConf;
 import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.core.persistence.api.ImplementationLookup;
 import org.apache.syncope.core.persistence.api.dao.AccountRule;
 import org.apache.syncope.core.persistence.api.dao.PasswordRule;
 import org.apache.syncope.core.persistence.api.dao.PullCorrelationRule;
+import org.apache.syncope.core.persistence.api.dao.PushCorrelationRule;
 import org.apache.syncope.core.persistence.api.dao.Reportlet;
 import org.apache.syncope.core.persistence.jpa.dao.DefaultAccountRule;
 import org.apache.syncope.core.persistence.jpa.dao.DefaultPasswordRule;
 import org.apache.syncope.core.persistence.jpa.dao.DefaultPullCorrelationRule;
+import org.apache.syncope.core.persistence.jpa.dao.DefaultPushCorrelationRule;
 import org.springframework.stereotype.Component;
 
 @Component
@@ -87,8 +90,14 @@ public class DummyImplementationLookup implements ImplementationLookup {
     }
 
     @Override
+    public Class<? extends PushCorrelationRule> getPushCorrelationRuleClass(
+            final Class<? extends PushCorrelationRuleConf> pushCorrelationRuleConfClass) {
+
+        return DefaultPushCorrelationRule.class;
+    }
+
+    @Override
     public Set<Class<?>> getAuditAppenderClasses() {
         return Collections.emptySet();
     }
-
 }
index dfa4ead..b3e050a 100644 (file)
@@ -26,6 +26,7 @@ import java.util.Optional;
 import org.apache.syncope.common.lib.policy.AccountRuleConf;
 import org.apache.syncope.common.lib.policy.PasswordRuleConf;
 import org.apache.syncope.common.lib.policy.PullCorrelationRuleConf;
+import org.apache.syncope.common.lib.policy.PushCorrelationRuleConf;
 import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.core.persistence.api.ImplementationLookup;
 import org.apache.syncope.core.persistence.api.dao.AccountRule;
@@ -33,6 +34,7 @@ import org.apache.syncope.core.persistence.api.dao.PasswordRule;
 import org.apache.syncope.core.persistence.api.dao.Reportlet;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
 import org.apache.syncope.core.persistence.api.dao.PullCorrelationRule;
+import org.apache.syncope.core.persistence.api.dao.PushCorrelationRule;
 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -183,6 +185,41 @@ public final class ImplementationManager {
         }
     }
 
+    public static Optional<PushCorrelationRule> buildPushCorrelationRule(final Implementation impl)
+            throws InstantiationException, IllegalAccessException {
+
+        switch (impl.getEngine()) {
+            case GROOVY:
+                return Optional.of(ImplementationManager.<PushCorrelationRule>buildGroovy(impl));
+
+            case JAVA:
+            default:
+                PushCorrelationRule rule = null;
+
+                PushCorrelationRuleConf ruleConf =
+                        POJOHelper.deserialize(impl.getBody(), PushCorrelationRuleConf.class);
+                Class<? extends PushCorrelationRule> ruleClass = ApplicationContextProvider.getApplicationContext().
+                        getBean(ImplementationLookup.class).getPushCorrelationRuleClass(ruleConf.getClass());
+                if (ruleClass == null) {
+                    LOG.warn("Could not find matching push correlation rule for {}", impl.getClass());
+                } else {
+                    // fetch (or create) rule
+                    if (ApplicationContextProvider.getBeanFactory().containsSingleton(ruleClass.getName())) {
+                        rule = (PushCorrelationRule) ApplicationContextProvider.getBeanFactory().
+                                getSingleton(ruleClass.getName());
+                    } else {
+                        rule = (PushCorrelationRule) ApplicationContextProvider.getBeanFactory().
+                                createBean(ruleClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
+                        ApplicationContextProvider.getBeanFactory().
+                                registerSingleton(ruleClass.getName(), rule);
+                    }
+                    rule.setConf(ruleConf);
+                }
+
+                return Optional.ofNullable(rule);
+        }
+    }
+
     public static <T> T build(final Implementation impl)
             throws InstantiationException, IllegalAccessException, ClassNotFoundException {
 
index a78c2ff..9bcdf44 100644 (file)
@@ -39,7 +39,7 @@ public interface JWTSSOProvider extends JwsSignatureVerifier {
 
     /**
      * Attempts to resolve the given JWT claims into internal {@link User} and authorities.
-     * <strong>IMPORTANT</strong>: this is not invoked for the {@code}admin{@code} super-user.
+     * <strong>IMPORTANT</strong>: this is not invoked for the {@code admin} super-user.
      *
      * @param jwtClaims JWT claims
      * @return internal User, with authorities, matching the provided JWT claims, if found; otherwise null
index 1f0d383..300220d 100644 (file)
@@ -23,12 +23,14 @@ import java.util.Set;
 import org.apache.syncope.common.lib.policy.AccountRuleConf;
 import org.apache.syncope.common.lib.policy.PasswordRuleConf;
 import org.apache.syncope.common.lib.policy.PullCorrelationRuleConf;
+import org.apache.syncope.common.lib.policy.PushCorrelationRuleConf;
 import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.common.lib.types.ImplementationType;
 import org.apache.syncope.core.persistence.api.ImplementationLookup;
 import org.apache.syncope.core.persistence.api.dao.AccountRule;
 import org.apache.syncope.core.persistence.api.dao.PasswordRule;
 import org.apache.syncope.core.persistence.api.dao.PullCorrelationRule;
+import org.apache.syncope.core.persistence.api.dao.PushCorrelationRule;
 import org.apache.syncope.core.persistence.api.dao.Reportlet;
 
 public class DummyImplementationLookup implements ImplementationLookup {
@@ -82,8 +84,14 @@ public class DummyImplementationLookup implements ImplementationLookup {
     }
 
     @Override
+    public Class<? extends PushCorrelationRule> getPushCorrelationRuleClass(
+            final Class<? extends PushCorrelationRuleConf> pushCorrelationRuleConfClass) {
+
+        return null;
+    }
+
+    @Override
     public Set<Class<?>> getAuditAppenderClasses() {
         return Collections.emptySet();
     }
-
 }
index 730f5e1..9e9d687 100644 (file)
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.core.persistence.jpa.validation.entity;
 
-import static org.apache.syncope.core.persistence.jpa.validation.entity.AbstractValidator.LOG;
-
 import javax.validation.ConstraintValidatorContext;
 import org.apache.syncope.common.lib.types.EntityViolationType;
 import org.apache.syncope.common.lib.types.ImplementationEngine;
index 2930038..e55033a 100644 (file)
@@ -31,5 +31,4 @@ public class DummyPullCorrelationRule implements PullCorrelationRule {
     public SearchCond getSearchCond(final ConnectorObject connObj, final Provision provision) {
         return new SearchCond();
     }
-
 }
index e544823..2c869f3 100644 (file)
  */
 package org.apache.syncope.fit.core.reference;
 
-import org.apache.syncope.common.lib.policy.AbstractPullCorrelationRuleConf;
+import org.apache.syncope.common.lib.policy.AbstractCorrelationRuleConf;
 import org.apache.syncope.common.lib.policy.PullCorrelationRuleConf;
 
 public class DummyPullCorrelationRuleConf
-        extends AbstractPullCorrelationRuleConf implements PullCorrelationRuleConf {
+        extends AbstractCorrelationRuleConf implements PullCorrelationRuleConf {
 
     private static final long serialVersionUID = -2984203196323732531L;
 
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DummyPushCorrelationRule.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DummyPushCorrelationRule.java
new file mode 100644 (file)
index 0000000..5fc3a7c
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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 org.apache.syncope.core.persistence.api.dao.PushCorrelationRule;
+import org.apache.syncope.core.persistence.api.dao.PushCorrelationRuleConfClass;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.resource.Provision;
+import org.identityconnectors.framework.common.objects.Uid;
+import org.identityconnectors.framework.common.objects.filter.Filter;
+import org.identityconnectors.framework.common.objects.filter.FilterBuilder;
+
+@PushCorrelationRuleConfClass(DummyPushCorrelationRuleConf.class)
+public class DummyPushCorrelationRule implements PushCorrelationRule {
+
+    @Override
+    public Filter getFilter(final Any<?> any, final Provision provision) {
+        return FilterBuilder.equalTo(new Uid(null));
+    }
+}
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DummyPushCorrelationRuleConf.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/DummyPushCorrelationRuleConf.java
new file mode 100644 (file)
index 0000000..f9453c7
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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 org.apache.syncope.common.lib.policy.AbstractCorrelationRuleConf;
+import org.apache.syncope.common.lib.policy.PushCorrelationRuleConf;
+
+public class DummyPushCorrelationRuleConf
+        extends AbstractCorrelationRuleConf implements PushCorrelationRuleConf {
+
+    private static final long serialVersionUID = -2984203196323732531L;
+
+}
index a08eaf8..ac20be8 100644 (file)
@@ -29,9 +29,11 @@ import org.apache.syncope.common.lib.policy.AccountRuleConf;
 import org.apache.syncope.common.lib.policy.DefaultAccountRuleConf;
 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.PasswordRuleConf;
 import org.apache.syncope.common.lib.policy.PullCorrelationRuleConf;
+import org.apache.syncope.common.lib.policy.PushCorrelationRuleConf;
 import org.apache.syncope.common.lib.report.AuditReportletConf;
 import org.apache.syncope.common.lib.report.GroupReportletConf;
 import org.apache.syncope.common.lib.report.ReconciliationReportletConf;
@@ -56,6 +58,7 @@ import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 import org.apache.syncope.core.persistence.api.dao.ImplementationDAO;
 import org.apache.syncope.core.persistence.api.dao.PasswordRule;
 import org.apache.syncope.core.persistence.api.dao.PullCorrelationRule;
+import org.apache.syncope.core.persistence.api.dao.PushCorrelationRule;
 import org.apache.syncope.core.persistence.api.dao.Reportlet;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.Implementation;
@@ -66,6 +69,7 @@ import org.apache.syncope.core.persistence.jpa.attrvalue.validation.EmailAddress
 import org.apache.syncope.core.persistence.jpa.dao.DefaultAccountRule;
 import org.apache.syncope.core.persistence.jpa.dao.DefaultPasswordRule;
 import org.apache.syncope.core.persistence.jpa.dao.DefaultPullCorrelationRule;
+import org.apache.syncope.core.persistence.jpa.dao.DefaultPushCorrelationRule;
 import org.apache.syncope.core.persistence.jpa.dao.HaveIBeenPwnedPasswordRule;
 import org.apache.syncope.core.provisioning.java.propagation.AzurePropagationActions;
 import org.apache.syncope.core.provisioning.java.propagation.DBPasswordPropagationActions;
@@ -128,7 +132,7 @@ public class ITImplementationLookup implements ImplementationLookup {
     };
 
     private static final Map<
-            Class<? extends PullCorrelationRuleConf>, Class<? extends PullCorrelationRule>> CORRELATION_RULE_CLASSES =
+            Class<? extends PullCorrelationRuleConf>, Class<? extends PullCorrelationRule>> PULL_CR_CLASSES =
             new HashMap<Class<? extends PullCorrelationRuleConf>, Class<? extends PullCorrelationRule>>() {
 
         private static final long serialVersionUID = 3109256773218160485L;
@@ -139,6 +143,18 @@ public class ITImplementationLookup implements ImplementationLookup {
         }
     };
 
+    private static final Map<
+            Class<? extends PushCorrelationRuleConf>, Class<? extends PushCorrelationRule>> PUSH_CR_CLASSES =
+            new HashMap<Class<? extends PushCorrelationRuleConf>, Class<? extends PushCorrelationRule>>() {
+
+        private static final long serialVersionUID = 3109256773218160485L;
+
+        {
+            put(DummyPushCorrelationRuleConf.class, DummyPushCorrelationRule.class);
+            put(DefaultPushCorrelationRuleConf.class, DefaultPushCorrelationRule.class);
+        }
+    };
+
     private static final Set<Class<?>> AUDITAPPENDER_CLASSES = new HashSet<>(
             Arrays.asList(TestFileAuditAppender.class, TestFileRewriteAuditAppender.class));
 
@@ -205,6 +221,10 @@ public class ITImplementationLookup implements ImplementationLookup {
             put(ImplementationType.PULL_CORRELATION_RULE, classNames);
 
             classNames = new HashSet<>();
+            classNames.add(DummyPushCorrelationRule.class.getName());
+            put(ImplementationType.PUSH_CORRELATION_RULE, classNames);
+
+            classNames = new HashSet<>();
             classNames.add(BasicValidator.class.getName());
             classNames.add(EmailAddressValidator.class.getName());
             classNames.add(AlwaysTrueValidator.class.getName());
@@ -308,7 +328,14 @@ public class ITImplementationLookup implements ImplementationLookup {
     public Class<? extends PullCorrelationRule> getPullCorrelationRuleClass(
             final Class<? extends PullCorrelationRuleConf> pullCorrelationRuleConfClass) {
 
-        return CORRELATION_RULE_CLASSES.get(pullCorrelationRuleConfClass);
+        return PULL_CR_CLASSES.get(pullCorrelationRuleConfClass);
+    }
+
+    @Override
+    public Class<? extends PushCorrelationRule> getPushCorrelationRuleClass(
+            final Class<? extends PushCorrelationRuleConf> pushCorrelationRuleConfClass) {
+
+        return PUSH_CR_CLASSES.get(pushCorrelationRuleConfClass);
     }
 
     @Override
index 9e7d541..c835759 100644 (file)
@@ -24,13 +24,15 @@ import static org.junit.jupiter.api.Assertions.fail;
 
 import java.io.InputStream;
 import java.net.URI;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 import java.util.Properties;
 import java.util.UUID;
 import javax.naming.Context;
 import javax.naming.NamingException;
-import javax.naming.directory.Attribute;
 import javax.naming.directory.BasicAttribute;
 import javax.naming.directory.DirContext;
 import javax.naming.directory.InitialDirContext;
@@ -254,7 +256,7 @@ public abstract class AbstractITCase {
     protected static SAML2SPService saml2SpService;
 
     protected static SAML2IdPService saml2IdPService;
-    
+
     protected static OIDCClientService oidcClientService;
 
     protected static OIDCProviderService oidcProviderService;
@@ -555,6 +557,7 @@ public abstract class AbstractITCase {
             ctx = getLdapResourceDirContext(bindDn, bindPwd);
             return ctx.lookup(objectDn);
         } catch (Exception e) {
+            LOG.error("Could not fetch {}", objectDn, e);
             return null;
         } finally {
             if (ctx != null) {
@@ -568,18 +571,23 @@ public abstract class AbstractITCase {
     }
 
     protected void updateLdapRemoteObject(
-            final String bindDn, final String bindPwd, final String objectDn, final Pair<String, String> attribute) {
+            final String bindDn,
+            final String bindPwd,
+            final String objectDn,
+            final Map<String, String> attributes) {
 
         InitialDirContext ctx = null;
         try {
             ctx = getLdapResourceDirContext(bindDn, bindPwd);
 
-            Attribute ldapAttribute = new BasicAttribute(attribute.getKey(), attribute.getValue());
-            ModificationItem[] item = new ModificationItem[1];
-            item[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, ldapAttribute);
-            ctx.modifyAttributes(objectDn, item);
+            List<ModificationItem> items = new ArrayList<>();
+            attributes.forEach((key, value) -> {
+                items.add(new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute(key, value)));
+            });
+
+            ctx.modifyAttributes(objectDn, items.toArray(new ModificationItem[] {}));
         } catch (Exception e) {
-            // ignore
+            LOG.error("While updating {} with {}", objectDn, attributes, e);
         } finally {
             if (ctx != null) {
                 try {
index 7ae759f..e47fcf7 100644 (file)
@@ -40,6 +40,7 @@ import org.apache.syncope.common.lib.policy.PullPolicyTO;
 import org.apache.syncope.common.lib.policy.DefaultAccountRuleConf;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf;
+import org.apache.syncope.common.lib.policy.PushPolicyTO;
 import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.syncope.common.lib.to.ImplementationTO;
 import org.apache.syncope.common.lib.types.ImplementationEngine;
@@ -48,6 +49,7 @@ 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.DummyPushCorrelationRule;
 import org.junit.jupiter.api.Test;
 
 public class PolicyITCase extends AbstractITCase {
@@ -79,6 +81,33 @@ public class PolicyITCase extends AbstractITCase {
         return policy;
     }
 
+    private PushPolicyTO buildPushPolicyTO() throws IOException {
+        ImplementationTO corrRule = null;
+        try {
+            corrRule = implementationService.read(ImplementationType.PUSH_CORRELATION_RULE, "TestPushRule");
+        } catch (SyncopeClientException e) {
+            if (e.getType().getResponseStatus() == Response.Status.NOT_FOUND) {
+                corrRule = new ImplementationTO();
+                corrRule.setKey("TestPushRule");
+                corrRule.setEngine(ImplementationEngine.GROOVY);
+                corrRule.setType(ImplementationType.PUSH_CORRELATION_RULE);
+                corrRule.setBody(IOUtils.toString(
+                        getClass().getResourceAsStream("/TestPushRule.groovy"), StandardCharsets.UTF_8));
+                Response response = implementationService.create(corrRule);
+                corrRule = implementationService.read(
+                        corrRule.getType(), response.getHeaderString(RESTHeaders.RESOURCE_KEY));
+                assertNotNull(corrRule);
+            }
+        }
+        assertNotNull(corrRule);
+
+        PushPolicyTO policy = new PushPolicyTO();
+        policy.getCorrelationRules().put(AnyTypeKind.USER.name(), corrRule.getKey());
+        policy.setDescription("Push policy");
+
+        return policy;
+    }
+
     @Test
     public void listByType() {
         List<PullPolicyTO> policyTOs = policyService.list(PolicyType.PULL);
@@ -115,9 +144,13 @@ public class PolicyITCase extends AbstractITCase {
 
     @Test
     public void create() throws IOException {
-        PullPolicyTO policyTO = createPolicy(PolicyType.PULL, buildPullPolicyTO());
-        assertNotNull(policyTO);
-        assertEquals("TestPullRule", policyTO.getCorrelationRules().get(AnyTypeKind.USER.name()));
+        PullPolicyTO pullPolicyTO = createPolicy(PolicyType.PULL, buildPullPolicyTO());
+        assertNotNull(pullPolicyTO);
+        assertEquals("TestPullRule", pullPolicyTO.getCorrelationRules().get(AnyTypeKind.USER.name()));
+
+        PushPolicyTO pushPolicyTO = createPolicy(PolicyType.PUSH, buildPushPolicyTO());
+        assertNotNull(pushPolicyTO);
+        assertEquals("TestPushRule", pushPolicyTO.getCorrelationRules().get(AnyTypeKind.USER.name()));
     }
 
     @Test
@@ -175,6 +208,14 @@ public class PolicyITCase extends AbstractITCase {
     }
 
     @Test
+    public void getPushCorrelationRuleJavaClasses() {
+        Set<String> classes = syncopeService.platform().
+                getJavaImplInfo(ImplementationType.PUSH_CORRELATION_RULE).get().getClasses();
+        assertEquals(1, classes.size());
+        assertEquals(DummyPushCorrelationRule.class.getName(), classes.iterator().next());
+    }
+
+    @Test
     public void issueSYNCOPE553() {
         AccountPolicyTO policy = new AccountPolicyTO();
         policy.setDescription("SYNCOPE553");
index 4edb57e..788ce04 100644 (file)
@@ -31,6 +31,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
+import java.util.Collections;
 import java.util.Date;
 import java.util.HashSet;
 import java.util.Locale;
@@ -1213,7 +1214,7 @@ public class PullTaskITCase extends AbstractTaskITCase {
 
             // 4. update the user on the external resource
             updateLdapRemoteObject(RESOURCE_LDAP_ADMIN_DN, RESOURCE_LDAP_ADMIN_PWD,
-                    userDn.getValues().get(0), Pair.of("mail", "pullFromLDAP2@syncope.apache.org"));
+                    userDn.getValues().get(0), Collections.singletonMap("mail", "pullFromLDAP2@syncope.apache.org"));
 
             connObject = resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), user.getKey());
             assertNotNull(connObject);
index 798008e..eaf6d4d 100644 (file)
@@ -25,7 +25,9 @@ import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
 
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 import javax.sql.DataSource;
 import javax.ws.rs.core.Response;
@@ -42,6 +44,7 @@ import org.apache.syncope.common.lib.to.NotificationTO;
 import org.apache.syncope.common.lib.to.NotificationTaskTO;
 import org.apache.syncope.common.lib.to.PlainSchemaTO;
 import org.apache.syncope.common.lib.to.ProvisionTO;
+import org.apache.syncope.common.lib.to.ReconStatus;
 import org.apache.syncope.common.lib.to.ResourceTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.AttrSchemaType;
@@ -269,6 +272,54 @@ public class PushTaskITCase extends AbstractTaskITCase {
     }
 
     @Test
+    public void pushPolicy() {
+        // 1. set push policy on ldap
+        ResourceTO ldap = resourceService.read(RESOURCE_NAME_LDAP);
+        assertNull(ldap.getPushPolicy());
+
+        ldap.setPushPolicy("fb6530e5-892d-4f47-a46b-180c5b6c5c83");
+        resourceService.update(ldap);
+
+        // 2. create push task with sole scope as the user 'vivaldi'
+        PushTaskTO sendVivaldi = new PushTaskTO();
+        sendVivaldi.setName("Send Vivaldi");
+        sendVivaldi.setResource(RESOURCE_NAME_LDAP);
+        sendVivaldi.setUnmatchingRule(UnmatchingRule.PROVISION);
+        sendVivaldi.setMatchingRule(MatchingRule.UPDATE);
+        sendVivaldi.setSourceRealm(SyncopeConstants.ROOT_REALM);
+        sendVivaldi.getFilters().put(AnyTypeKind.GROUP.name(), "name==$null");
+        sendVivaldi.getFilters().put(AnyTypeKind.USER.name(), "username==vivaldi");
+        sendVivaldi.setPerformCreate(true);
+        sendVivaldi.setPerformUpdate(true);
+
+        Response response = taskService.create(TaskType.PUSH, sendVivaldi);
+        sendVivaldi = getObject(response.getLocation(), TaskService.class, PushTaskTO.class);
+        assertNotNull(sendVivaldi);
+
+        // 3. execute push: vivaldi is found on ldap
+        execProvisioningTask(taskService, TaskType.PUSH, sendVivaldi.getKey(), 50, false);
+
+        ReconStatus status = reconciliationService.status(AnyTypeKind.USER, "vivaldi", RESOURCE_NAME_LDAP);
+        assertNotNull(status.getOnResource());
+
+        // 4. update vivaldi on ldap: reconciliation status does not find it anymore, as remote key was changed
+        Map<String, String> attrs = new HashMap<>();
+        attrs.put("cn", "vivaldiZZ");
+        attrs.put("mail", "vivaldi@syncope.org");
+        updateLdapRemoteObject(RESOURCE_LDAP_ADMIN_DN, RESOURCE_LDAP_ADMIN_PWD, "uid=vivaldi,ou=People,o=isp", attrs);
+
+        status = reconciliationService.status(AnyTypeKind.USER, "vivaldi", RESOURCE_NAME_LDAP);
+        assertNull(status.getOnResource());
+
+        // 5. execute push again: the push policy will find anyway vivaldi because of the email attribute
+        execProvisioningTask(taskService, TaskType.PUSH, sendVivaldi.getKey(), 50, false);
+
+        // 6. now the reconciliation status is fine again, as the push above did overwrite the entry on ldap
+        status = reconciliationService.status(AnyTypeKind.USER, "vivaldi", RESOURCE_NAME_LDAP);
+        assertNotNull(status.getOnResource());
+    }
+
+    @Test
     public void orgUnit() {
         assertNull(getLdapRemoteObject(RESOURCE_LDAP_ADMIN_DN, RESOURCE_LDAP_ADMIN_PWD, "ou=odd,o=isp"));
         assertNull(getLdapRemoteObject(RESOURCE_LDAP_ADMIN_DN, RESOURCE_LDAP_ADMIN_PWD, "ou=even,o=isp"));
index 04b1d9e..2c8308f 100644 (file)
@@ -25,7 +25,7 @@ import org.apache.syncope.core.persistence.api.entity.resource.Provision
 import org.identityconnectors.framework.common.objects.ConnectorObject;
 
 /**
- * Test pull rule relying on <tt>email</tt> attribute value.
+ * Test pull rule relying on {@code email} attribute value.
  */
 @CompileStatic
 class TestPullRule implements PullCorrelationRule {
diff --git a/fit/core-reference/src/test/resources/TestPushRule.groovy b/fit/core-reference/src/test/resources/TestPushRule.groovy
new file mode 100644 (file)
index 0000000..320b588
--- /dev/null
@@ -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.
+ */
+import groovy.transform.CompileStatic;
+import org.apache.syncope.core.persistence.api.dao.PushCorrelationRule;
+import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.resource.Provision;
+import org.identityconnectors.framework.common.objects.AttributeBuilder;
+import org.identityconnectors.framework.common.objects.filter.Filter;
+import org.identityconnectors.framework.common.objects.filter.FilterBuilder;
+
+/**
+ * Test push rule relying on {@code email} attribute value.
+ */
+@CompileStatic
+class TestPushRule implements PushCorrelationRule {
+
+  @Override
+  Filter getFilter(final Any<?> any, final Provision provision) {
+    return FilterBuilder.equalTo(
+      AttributeBuilder.build("email", any.getPlainAttr("email").get().getValuesAsStrings().get(0)));
+  }
+}
index ebae238..d5463ce 100644 (file)
@@ -267,12 +267,13 @@ different rule is required
 Pull correlation rules define how to match objects received from <<connector-instance-details,connector instances>>
 with existing Users, Groups or Any Objects.
 
-The 
+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/PlainAttrsPullCorrelationRule.java[default^]
+https://github.com/apache/syncope/blob/syncope-{docVersion}/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPullCorrelationRule.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/PlainAttrsPullCorrelationRule.java[default^]
+https://github.com/apache/syncope/blob/master/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPullCorrelationRule.java[default^]
 endif::[]
 implementation attempts to match entities on the basis of the values of the provided plain attributes,
 according to the available <<mapping,mapping>>.
@@ -281,10 +282,10 @@ according to the available <<mapping,mapping>>.
 ====
 Custom pull correlation rules can be provided by <<implementations,implementing>> 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/PullCorrelationRule.java[PullCorrelationRule^]
+https://github.com/apache/syncope/blob/syncope-{docVersion}/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PullCorrelationRule.java[PullCorrelationRule^]
 endif::[]
 ifeval::["{snapshotOrRelease}" == "snapshot"]
-https://github.com/apache/syncope/blob/master/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/pushpull/PullCorrelationRule.java[PullCorrelationRule^]
+https://github.com/apache/syncope/blob/master/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PullCorrelationRule.java[PullCorrelationRule^]
 endif::[]
 interface.
 ====
@@ -298,3 +299,31 @@ Push policies are evaluated during the execution of <<tasks-push,push tasks>>.
 ====
 When set for resource R, a push policy is enforced on all Users, Groups and Any Objects pushed to R.
 ====
+
+===== Push Correlation Rules
+
+Push correlation rules define how to match existing Users, Groups or Any Objects with objects received from
+<<connector-instance-details,connector instances>>.
+
+The
+ifeval::["{snapshotOrRelease}" == "release"]
+https://github.com/apache/syncope/blob/syncope-{docVersion}/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPushCorrelationRule.java[default^]
+]
+endif::[]
+ifeval::["{snapshotOrRelease}" == "snapshot"]
+https://github.com/apache/syncope/blob/master/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPushCorrelationRule.java[default^]
+endif::[]
+implementation attempts to match entities on the basis of the values of the provided plain attributes,
+according to the available <<mapping,mapping>>.
+
+[TIP]
+====
+Custom push correlation rules can be provided by <<implementations,implementing>> the
+ifeval::["{snapshotOrRelease}" == "release"]
+https://github.com/apache/syncope/blob/syncope-{docVersion}/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PushCorrelationRule.java[PushCorrelationRule^]
+endif::[]
+ifeval::["{snapshotOrRelease}" == "snapshot"]
+https://github.com/apache/syncope/blob/master/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PushCorrelationRule.java[PushCorrelationRule^]
+endif::[]
+interface.
+====