You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by fm...@apache.org on 2013/02/04 19:22:01 UTC

svn commit: r1442286 - in /syncope/trunk: common/src/main/java/org/apache/syncope/common/annotation/ common/src/main/java/org/apache/syncope/common/types/ console/src/main/java/org/apache/syncope/console/pages/panels/ console/src/main/resources/org/apa...

Author: fmartelli
Date: Mon Feb  4 18:22:00 2013
New Revision: 1442286

URL: http://svn.apache.org/viewvc?rev=1442286&view=rev
Log:
SYNCOPE-258: provided feature on the core; the next step will be done on the console ...

Added:
    syncope/trunk/common/src/main/java/org/apache/syncope/common/annotation/ClassList.java
      - copied, changed from r1439893, syncope/trunk/common/src/main/java/org/apache/syncope/common/annotation/SchemaList.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncRule.java
      - copied, changed from r1441451, syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncActions.java
    syncope/trunk/core/src/test/java/org/apache/syncope/core/sync/TestSyncRule.java   (with props)
Modified:
    syncope/trunk/common/src/main/java/org/apache/syncope/common/types/AuditElements.java
    syncope/trunk/common/src/main/java/org/apache/syncope/common/types/SyncPolicySpec.java
    syncope/trunk/console/src/main/java/org/apache/syncope/console/pages/panels/PolicyBeanPanel.java
    syncope/trunk/console/src/main/resources/org/apache/syncope/console/pages/PolicyModalPage.properties
    syncope/trunk/core/pom.xml
    syncope/trunk/core/src/main/java/org/apache/syncope/core/init/ImplementationClassNamesLoader.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/persistence/beans/Policy.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/persistence/beans/SyncPolicy.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/PolicyController.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/SyncopeSyncResultHandler.java
    syncope/trunk/core/src/main/java/org/apache/syncope/core/util/AttributableUtil.java
    syncope/trunk/core/src/test/java/org/apache/syncope/core/persistence/dao/PolicyTest.java
    syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/PolicyTestITCase.java
    syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java
    syncope/trunk/core/src/test/resources/content.xml

Copied: syncope/trunk/common/src/main/java/org/apache/syncope/common/annotation/ClassList.java (from r1439893, syncope/trunk/common/src/main/java/org/apache/syncope/common/annotation/SchemaList.java)
URL: http://svn.apache.org/viewvc/syncope/trunk/common/src/main/java/org/apache/syncope/common/annotation/ClassList.java?p2=syncope/trunk/common/src/main/java/org/apache/syncope/common/annotation/ClassList.java&p1=syncope/trunk/common/src/main/java/org/apache/syncope/common/annotation/SchemaList.java&r1=1439893&r2=1442286&rev=1442286&view=diff
==============================================================================
--- syncope/trunk/common/src/main/java/org/apache/syncope/common/annotation/SchemaList.java (original)
+++ syncope/trunk/common/src/main/java/org/apache/syncope/common/annotation/ClassList.java Mon Feb  4 18:22:00 2013
@@ -22,7 +22,5 @@ import java.lang.annotation.Retention;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 @Retention(RUNTIME)
-public @interface SchemaList {
-
-    boolean extended() default false;
+public @interface ClassList {
 }

Modified: syncope/trunk/common/src/main/java/org/apache/syncope/common/types/AuditElements.java
URL: http://svn.apache.org/viewvc/syncope/trunk/common/src/main/java/org/apache/syncope/common/types/AuditElements.java?rev=1442286&r1=1442285&r2=1442286&view=diff
==============================================================================
--- syncope/trunk/common/src/main/java/org/apache/syncope/common/types/AuditElements.java (original)
+++ syncope/trunk/common/src/main/java/org/apache/syncope/common/types/AuditElements.java Mon Feb  4 18:22:00 2013
@@ -19,7 +19,6 @@
 package org.apache.syncope.common.types;
 
 import java.util.EnumSet;
-
 import javax.xml.bind.annotation.XmlEnum;
 
 public final class AuditElements {
@@ -135,7 +134,8 @@ public final class AuditElements {
         create,
         read,
         update,
-        delete
+        delete,
+        getCorrelationRuleClasses
 
     }
 

Modified: syncope/trunk/common/src/main/java/org/apache/syncope/common/types/SyncPolicySpec.java
URL: http://svn.apache.org/viewvc/syncope/trunk/common/src/main/java/org/apache/syncope/common/types/SyncPolicySpec.java?rev=1442286&r1=1442285&r2=1442286&view=diff
==============================================================================
--- syncope/trunk/common/src/main/java/org/apache/syncope/common/types/SyncPolicySpec.java (original)
+++ syncope/trunk/common/src/main/java/org/apache/syncope/common/types/SyncPolicySpec.java Mon Feb  4 18:22:00 2013
@@ -20,11 +20,10 @@ package org.apache.syncope.common.types;
 
 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.XmlType;
-
+import org.apache.syncope.common.annotation.ClassList;
 import org.apache.syncope.common.annotation.SchemaList;
 
 @XmlType
@@ -38,12 +37,18 @@ public class SyncPolicySpec extends Abst
     @SchemaList(extended = true)
     private List<String> uAltSearchSchemas;
 
+    @ClassList
+    private String userJavaRule;
+
     /**
      * SyncopeRole attributes and fields for matching during synchronization.
      */
     @SchemaList(extended = true)
     private List<String> rAltSearchSchemas;
 
+    @ClassList
+    private String roleJavaRule;
+
     /**
      * Conflict resolution action.
      */
@@ -85,4 +90,24 @@ public class SyncPolicySpec extends Abst
     public void setrAltSearchSchemas(List<String> rAltSearchSchemas) {
         this.rAltSearchSchemas = rAltSearchSchemas;
     }
+
+    @XmlElementWrapper(name = "roleJavaRule")
+    @XmlElement(name = "roleJavaRule")
+    public String getRoleJavaRule() {
+        return roleJavaRule;
+    }
+
+    public void setRoleJavaRule(String roleJavaRule) {
+        this.roleJavaRule = roleJavaRule;
+    }
+
+    @XmlElementWrapper(name = "userJavaRule")
+    @XmlElement(name = "userJavaRule")
+    public String getUserJavaRule() {
+        return userJavaRule;
+    }
+
+    public void setUserJavaRule(String userJavaRule) {
+        this.userJavaRule = userJavaRule;
+    }
 }

Modified: syncope/trunk/console/src/main/java/org/apache/syncope/console/pages/panels/PolicyBeanPanel.java
URL: http://svn.apache.org/viewvc/syncope/trunk/console/src/main/java/org/apache/syncope/console/pages/panels/PolicyBeanPanel.java?rev=1442286&r1=1442285&r2=1442286&view=diff
==============================================================================
--- syncope/trunk/console/src/main/java/org/apache/syncope/console/pages/panels/PolicyBeanPanel.java (original)
+++ syncope/trunk/console/src/main/java/org/apache/syncope/console/pages/panels/PolicyBeanPanel.java Mon Feb  4 18:22:00 2013
@@ -99,7 +99,6 @@ public class PolicyBeanPanel extends Pan
                 fieldWrapper.setType(field.getType());
 
                 final SchemaList schemaList = field.getAnnotation(SchemaList.class);
-
                 fieldWrapper.setSchemaList(schemaList);
 
                 items.add(fieldWrapper);

Modified: syncope/trunk/console/src/main/resources/org/apache/syncope/console/pages/PolicyModalPage.properties
URL: http://svn.apache.org/viewvc/syncope/trunk/console/src/main/resources/org/apache/syncope/console/pages/PolicyModalPage.properties?rev=1442286&r1=1442285&r2=1442286&view=diff
==============================================================================
--- syncope/trunk/console/src/main/resources/org/apache/syncope/console/pages/PolicyModalPage.properties (original)
+++ syncope/trunk/console/src/main/resources/org/apache/syncope/console/pages/PolicyModalPage.properties Mon Feb  4 18:22:00 2013
@@ -71,6 +71,9 @@ permittedLoginRetries=Maximum number of 
 # Sync policy specification properties
 #-----------------------------
 uAltSearchSchemas=Alternative attributes for local search (users)
+rAltSearchSchemas=Alternative attributes for local search (roles)
+userJavaRule=Custom user correlation rule
+roleJavaRule=Custom role correlation rule
 conflictResolutionAction= Conflict resolution action
 #-----------------------------
 
@@ -84,4 +87,3 @@ GLOBAL_PASSWORD=Global Password Policy
 SYNC=Synchronization Policy
 GLOBAL_SYNC=Global Synchronization Policy
 #-----------------------------
-rAltSearchSchemas=Alternative attributes for local search (roles)

Modified: syncope/trunk/core/pom.xml
URL: http://svn.apache.org/viewvc/syncope/trunk/core/pom.xml?rev=1442286&r1=1442285&r2=1442286&view=diff
==============================================================================
--- syncope/trunk/core/pom.xml (original)
+++ syncope/trunk/core/pom.xml Mon Feb  4 18:22:00 2013
@@ -442,6 +442,8 @@ under the License.
                 </unzip>
                 <copy file="${project.build.directory}/test-classes/org/apache/syncope/core/sync/TestSyncActions.class" 
                       todir="${cargo.run.dir}/WEB-INF/classes/org/apache/syncope/core/sync"/>
+                <copy file="${project.build.directory}/test-classes/org/apache/syncope/core/sync/TestSyncRule.class" 
+                      todir="${cargo.run.dir}/WEB-INF/classes/org/apache/syncope/core/sync"/>
                 <copy file="${project.build.directory}/test-classes/db.jsp" todir="${cargo.run.dir}"/>
                 <copy todir="${cargo.run.dir}/WEB-INF/classes" includeEmptyDirs="false">
                   <fileset dir="${project.build.directory}/test-classes">

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/init/ImplementationClassNamesLoader.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/init/ImplementationClassNamesLoader.java?rev=1442286&r1=1442285&r2=1442286&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/init/ImplementationClassNamesLoader.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/init/ImplementationClassNamesLoader.java Mon Feb  4 18:22:00 2013
@@ -31,6 +31,7 @@ import org.apache.syncope.core.propagati
 import org.apache.syncope.core.report.ReportJob;
 import org.apache.syncope.core.report.Reportlet;
 import org.apache.syncope.core.sync.SyncActions;
+import org.apache.syncope.core.sync.SyncRule;
 import org.apache.syncope.core.sync.impl.SyncJob;
 import org.quartz.Job;
 import org.slf4j.Logger;
@@ -54,6 +55,7 @@ public class ImplementationClassNamesLoa
         REPORTLET,
         JOB,
         SYNC_ACTIONS,
+        SYNC_CORRELATION_RULES,
         PROPAGATION_ACTIONS,
         VALIDATOR
 
@@ -100,6 +102,10 @@ public class ImplementationClassNamesLoa
                     if (interfaces.contains(SyncActions.class) && !metadata.isAbstract()) {
                         classNames.get(Type.SYNC_ACTIONS).add(metadata.getClassName());
                     }
+                    
+                    if (interfaces.contains(SyncRule.class) && !metadata.isAbstract()) {
+                        classNames.get(Type.SYNC_CORRELATION_RULES).add(metadata.getClassName());
+                    }
 
                     if (interfaces.contains(PropagationActions.class) && !metadata.isAbstract()) {
                         classNames.get(Type.PROPAGATION_ACTIONS).add(metadata.getClassName());

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/persistence/beans/Policy.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/persistence/beans/Policy.java?rev=1442286&r1=1442285&r2=1442286&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/persistence/beans/Policy.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/persistence/beans/Policy.java Mon Feb  4 18:22:00 2013
@@ -24,7 +24,6 @@ import javax.persistence.Enumerated;
 import javax.persistence.Id;
 import javax.persistence.Lob;
 import javax.validation.constraints.NotNull;
-
 import org.apache.syncope.common.types.AbstractPolicySpec;
 import org.apache.syncope.common.types.PolicyType;
 import org.apache.syncope.core.persistence.validation.entity.PolicyCheck;

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/persistence/beans/SyncPolicy.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/persistence/beans/SyncPolicy.java?rev=1442286&r1=1442285&r2=1442286&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/persistence/beans/SyncPolicy.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/persistence/beans/SyncPolicy.java Mon Feb  4 18:22:00 2013
@@ -19,7 +19,6 @@
 package org.apache.syncope.core.persistence.beans;
 
 import javax.persistence.Entity;
-
 import org.apache.syncope.common.types.PolicyType;
 
 @Entity

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/PolicyController.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/PolicyController.java?rev=1442286&r1=1442285&r2=1442286&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/PolicyController.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/rest/controller/PolicyController.java Mon Feb  4 18:22:00 2013
@@ -21,6 +21,7 @@ package org.apache.syncope.core.rest.con
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
+import java.util.Set;
 
 import javax.servlet.http.HttpServletResponse;
 
@@ -28,12 +29,14 @@ import org.apache.syncope.common.to.Acco
 import org.apache.syncope.common.to.PasswordPolicyTO;
 import org.apache.syncope.common.to.PolicyTO;
 import org.apache.syncope.common.to.SyncPolicyTO;
+import org.apache.syncope.common.types.AuditElements;
 import org.apache.syncope.common.types.AuditElements.Category;
 import org.apache.syncope.common.types.AuditElements.PolicySubCategory;
 import org.apache.syncope.common.types.AuditElements.Result;
 import org.apache.syncope.common.types.PolicyType;
 import org.apache.syncope.common.validation.SyncopeClientCompositeErrorException;
 import org.apache.syncope.core.audit.AuditManager;
+import org.apache.syncope.core.init.ImplementationClassNamesLoader;
 import org.apache.syncope.core.persistence.beans.AccountPolicy;
 import org.apache.syncope.core.persistence.beans.PasswordPolicy;
 import org.apache.syncope.core.persistence.beans.Policy;
@@ -48,12 +51,16 @@ import org.springframework.web.bind.anno
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.servlet.ModelAndView;
 
 @Controller
 @RequestMapping("/policy")
 public class PolicyController extends AbstractController {
 
     @Autowired
+    private ImplementationClassNamesLoader classNamesLoader;
+
+    @Autowired
     private AuditManager auditManager;
 
     @Autowired
@@ -166,7 +173,8 @@ public class PolicyController extends Ab
 
     @PreAuthorize("hasRole('POLICY_READ')")
     @RequestMapping(method = RequestMethod.GET, value = "/password/global/read")
-    public PasswordPolicyTO getGlobalPasswordPolicy() throws NotFoundException {
+    public PasswordPolicyTO getGlobalPasswordPolicy()
+            throws NotFoundException {
 
         LOG.debug("Reading global password policy");
 
@@ -183,7 +191,8 @@ public class PolicyController extends Ab
 
     @PreAuthorize("hasRole('POLICY_READ')")
     @RequestMapping(method = RequestMethod.GET, value = "/account/global/read")
-    public AccountPolicyTO getGlobalAccountPolicy() throws NotFoundException {
+    public AccountPolicyTO getGlobalAccountPolicy()
+            throws NotFoundException {
 
         LOG.debug("Reading global account policy");
 
@@ -200,7 +209,8 @@ public class PolicyController extends Ab
 
     @PreAuthorize("hasRole('POLICY_READ')")
     @RequestMapping(method = RequestMethod.GET, value = "/sync/global/read")
-    public SyncPolicyTO getGlobalSyncPolicy() throws NotFoundException {
+    public SyncPolicyTO getGlobalSyncPolicy()
+            throws NotFoundException {
 
         LOG.debug("Reading global sync policy");
 
@@ -235,7 +245,8 @@ public class PolicyController extends Ab
 
     @PreAuthorize("hasRole('POLICY_DELETE')")
     @RequestMapping(method = RequestMethod.GET, value = "/delete/{id}")
-    public PolicyTO delete(@PathVariable("id") final Long id) throws NotFoundException {
+    public PolicyTO delete(@PathVariable("id") final Long id)
+            throws NotFoundException {
         Policy policy = policyDAO.find(id);
         if (policy == null) {
             throw new NotFoundException("Policy " + id + " not found");
@@ -249,4 +260,16 @@ public class PolicyController extends Ab
 
         return policyToDelete;
     }
+
+    @PreAuthorize("hasRole('POLICY_LIST')")
+    @RequestMapping(method = RequestMethod.GET, value = "/correlationRuleClasses")
+    public ModelAndView getCorrelationRuleClasses() {
+        final Set<String> actionsClasses =
+                classNamesLoader.getClassNames(ImplementationClassNamesLoader.Type.SYNC_CORRELATION_RULES);
+
+        auditManager.audit(Category.policy, AuditElements.PolicySubCategory.getCorrelationRuleClasses,
+                Result.success, "Successfully listed all correlation rule classes: " + actionsClasses.size());
+
+        return new ModelAndView().addObject(actionsClasses);
+    }
 }

Copied: syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncRule.java (from r1441451, syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncActions.java)
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncRule.java?p2=syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncRule.java&p1=syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncActions.java&r1=1441451&r2=1442286&rev=1442286&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncActions.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/SyncRule.java Mon Feb  4 18:22:00 2013
@@ -18,83 +18,19 @@
  */
 package org.apache.syncope.core.sync;
 
-import java.util.List;
-
-import org.apache.syncope.common.mod.AbstractAttributableMod;
-import org.apache.syncope.common.to.AbstractAttributableTO;
-import org.identityconnectors.framework.common.objects.SyncDelta;
-import org.identityconnectors.framework.common.objects.SyncResultsHandler;
-import org.quartz.JobExecutionException;
+import org.apache.syncope.common.search.NodeCond;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
 
 /**
- * Interface for actions to be performed during SyncJob execution.
+ * Interface for correlation rule to be evaluated during SyncJob execution.
  */
-public interface SyncActions {
-
-    /**
-     * Action to be executed before to start the synchronization task execution.
-     *
-     * @param handler synchronization handler being executed.
-     * @throws JobExecutionException in case of generic failure.
-     */
-    void beforeAll(final SyncResultsHandler handler) throws JobExecutionException;
-
-    /**
-     * Action to be executed before to create a synchronized user locally.
-     *
-     * @param handler synchronization handler being executed.
-     * @param delta retrieved synchronization information
-     * @param subject user / role to be created
-     * @return synchronization information used for user status evaluation and to be passed to the 'after' method.
-     * @throws JobExecutionException in case of generic failure
-     */
-    <T extends AbstractAttributableTO> SyncDelta beforeCreate(final SyncResultsHandler handler,
-            final SyncDelta delta, final T subject) throws JobExecutionException;
-
-    /**
-     * Action to be executed before to update a synchronized user locally.
-     *
-     * @param handler synchronization handler being executed.
-     * @param delta retrieved synchronization information
-     * @param subject local user / role information
-     * @param subjectMod modification
-     * @return synchronization information used for logging and to be passed to the 'after' method.
-     * @throws JobExecutionException in case of generic failure.
-     */
-    <T extends AbstractAttributableTO, K extends AbstractAttributableMod> SyncDelta beforeUpdate(
-            final SyncResultsHandler handler, final SyncDelta delta, final T subject, final K subjectMod)
-            throws JobExecutionException;
-
-    /**
-     * Action to be executed before to delete a synchronized user locally.
-     *
-     * @param handler synchronization handler being executed.
-     * @param delta retrieved synchronization information
-     * @param subject lcao user / role to be deleted
-     * @return synchronization information used for logging and to be passed to the 'after' method.
-     * @throws JobExecutionException in case of generic failure
-     */
-    <T extends AbstractAttributableTO> SyncDelta beforeDelete(final SyncResultsHandler handler,
-            final SyncDelta delta, final T subject) throws JobExecutionException;
-
-    /**
-     * Action to be executed after each local user synchronization.
-     *
-     * @param handler synchronization handler being executed.
-     * @param delta retrieved synchronization information (may be modified by 'beforeCreate/beforeUpdate/beforeDelete')
-     * @param subject synchronized local user / role
-     * @param result global synchronization results at the current synchronization step
-     * @throws JobExecutionException in case of generic failure
-     */
-    <T extends AbstractAttributableTO> void after(final SyncResultsHandler handler, final SyncDelta delta,
-            final T subject, final SyncResult result) throws JobExecutionException;
+public interface SyncRule {
 
     /**
-     * Action to be executed after the synchronization task completion.
+     * Return a search condition.
      *
-     * @param handler synchronization handler being executed.
-     * @param results synchronization result
-     * @throws JobExecutionException in case of generic failure
+     * @param connObj connector object.
+     * @return search condition.
      */
-    void afterAll(final SyncResultsHandler handler, final List<SyncResult> results) throws JobExecutionException;
+    NodeCond getSearchCond(final ConnectorObject connObj);
 }

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/SyncopeSyncResultHandler.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/SyncopeSyncResultHandler.java?rev=1442286&r1=1442285&r2=1442286&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/SyncopeSyncResultHandler.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/sync/impl/SyncopeSyncResultHandler.java Mon Feb  4 18:22:00 2013
@@ -69,6 +69,7 @@ import org.apache.syncope.core.rest.data
 import org.apache.syncope.core.rest.data.UserDataBinder;
 import org.apache.syncope.core.sync.SyncActions;
 import org.apache.syncope.core.sync.SyncResult;
+import org.apache.syncope.core.sync.SyncRule;
 import org.apache.syncope.core.util.AttributableUtil;
 import org.apache.syncope.core.util.EntitlementUtil;
 import org.apache.syncope.core.workflow.WorkflowResult;
@@ -332,10 +333,13 @@ public class SyncopeSyncResultHandler im
         return result;
     }
 
-    private List<Long> findByAttributableSearch(final ConnectorObject connObj, final SyncPolicySpec policySpec,
-            final AttributableUtil attrUtil) {
+    private List<Long> findByCorrelationRule(
+            final ConnectorObject connObj, final SyncRule rule, final AttributableUtil attrUtil) {
+        return search(rule.getSearchCond(connObj), attrUtil);
+    }
 
-        final List<Long> result = new ArrayList<Long>();
+    private List<Long> findByAttributableSearch(
+            final ConnectorObject connObj, final List<String> altSearchSchemas, final AttributableUtil attrUtil) {
 
         // search for external attribute's name/value of each specified name
 
@@ -348,7 +352,7 @@ public class SyncopeSyncResultHandler im
         // search for user/role by attribute(s) specified in the policy
         NodeCond searchCond = null;
 
-        for (String schema : attrUtil.getAltSearchSchemas(policySpec)) {
+        for (String schema : altSearchSchemas) {
             Attribute value = extValues.get(schema);
 
             AttributeCond.Type type;
@@ -390,6 +394,12 @@ public class SyncopeSyncResultHandler im
                     : NodeCond.getAndCond(searchCond, nodeCond);
         }
 
+        return search(searchCond, attrUtil);
+    }
+
+    private List<Long> search(final NodeCond searchCond, final AttributableUtil attrUtil) {
+        final List<Long> result = new ArrayList<Long>();
+
         final List<AbstractAttributable> subjects = searchDAO.search(
                 EntitlementUtil.getRoleIds(entitlementDAO.findAll()), searchCond, attrUtil);
         for (AbstractAttributable subject : subjects) {
@@ -413,9 +423,18 @@ public class SyncopeSyncResultHandler im
             policySpec = (SyncPolicySpec) syncTask.getResource().getSyncPolicy().getSpecification();
         }
 
-        return policySpec == null || attrUtil.getAltSearchSchemas(policySpec).isEmpty()
+        SyncRule syncRule = null;
+        List<String> altSearchSchemas = null;
+
+        if (policySpec != null) {
+            syncRule = attrUtil.getCorrelationRule(policySpec);
+            altSearchSchemas = attrUtil.getAltSearchSchemas(policySpec);
+        }
+        
+        return syncRule == null ? altSearchSchemas == null
                 ? findByAccountIdItem(uid, attrUtil)
-                : findByAttributableSearch(connObj, policySpec, attrUtil);
+                : findByAttributableSearch(connObj, altSearchSchemas, attrUtil)
+                : findByCorrelationRule(connObj, syncRule, attrUtil);
     }
 
     public Long findMatchingAttributableId(final ObjectClass objectClass, final String name) {
@@ -645,7 +664,8 @@ public class SyncopeSyncResultHandler im
     }
 
     protected List<SyncResult> update(SyncDelta delta, final List<Long> subjects, final AttributableUtil attrUtil,
-            final boolean dryRun) throws JobExecutionException {
+            final boolean dryRun)
+            throws JobExecutionException {
 
         if (!syncTask.isPerformUpdate()) {
             LOG.debug("SyncTask not configured for update");

Modified: syncope/trunk/core/src/main/java/org/apache/syncope/core/util/AttributableUtil.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/main/java/org/apache/syncope/core/util/AttributableUtil.java?rev=1442286&r1=1442285&r2=1442286&view=diff
==============================================================================
--- syncope/trunk/core/src/main/java/org/apache/syncope/core/util/AttributableUtil.java (original)
+++ syncope/trunk/core/src/main/java/org/apache/syncope/core/util/AttributableUtil.java Mon Feb  4 18:22:00 2013
@@ -20,6 +20,7 @@ package org.apache.syncope.core.util;
 
 import java.util.Collections;
 import java.util.List;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.to.AbstractAttributableTO;
 import org.apache.syncope.common.to.MembershipTO;
 import org.apache.syncope.common.to.RoleTO;
@@ -66,11 +67,18 @@ import org.apache.syncope.core.persisten
 import org.apache.syncope.core.persistence.beans.user.USchema;
 import org.apache.syncope.core.persistence.beans.user.UVirAttr;
 import org.apache.syncope.core.persistence.beans.user.UVirSchema;
+import org.apache.syncope.core.sync.SyncRule;
 import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.slf4j.LoggerFactory;
 
 @SuppressWarnings("unchecked")
 public class AttributableUtil {
 
+    /**
+     * Logger.
+     */
+    protected static final org.slf4j.Logger LOG = LoggerFactory.getLogger(AttributableUtil.class);
+
     private final AttributableType type;
 
     public static AttributableUtil getInstance(final AttributableType type) {
@@ -579,22 +587,49 @@ public class AttributableUtil {
     public List<String> getAltSearchSchemas(final SyncPolicySpec policySpec) {
         List<String> result = Collections.EMPTY_LIST;
 
-        if (policySpec != null) {
-            switch (type) {
-                case USER:
-                    result = policySpec.getuAltSearchSchemas();
-                    break;
-                case ROLE:
-                    result = policySpec.getrAltSearchSchemas();
-                    break;
-                case MEMBERSHIP:
-                default:
-            }
+        switch (type) {
+            case USER:
+                result = policySpec.getuAltSearchSchemas();
+                break;
+            case ROLE:
+                result = policySpec.getrAltSearchSchemas();
+                break;
+            case MEMBERSHIP:
+            default:
         }
 
         return result;
     }
 
+    public SyncRule getCorrelationRule(final SyncPolicySpec policySpec) {
+
+        String clazz;
+
+        switch (type) {
+            case USER:
+                clazz = policySpec.getUserJavaRule();
+                break;
+            case ROLE:
+                clazz = policySpec.getRoleJavaRule();
+                break;
+            case MEMBERSHIP:
+            default:
+                clazz = null;
+        }
+
+        SyncRule res = null;
+
+        if (StringUtils.isNotBlank(clazz)) {
+            try {
+                res = (SyncRule) Class.forName(clazz).newInstance();
+            } catch (Exception e) {
+                LOG.error("Failure instantiating correlation rule class '{}'", clazz, e);
+            }
+        }
+
+        return res;
+    }
+
     public String searchView() {
         String result = "";
 

Modified: syncope/trunk/core/src/test/java/org/apache/syncope/core/persistence/dao/PolicyTest.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/persistence/dao/PolicyTest.java?rev=1442286&r1=1442285&r2=1442286&view=diff
==============================================================================
--- syncope/trunk/core/src/test/java/org/apache/syncope/core/persistence/dao/PolicyTest.java (original)
+++ syncope/trunk/core/src/test/java/org/apache/syncope/core/persistence/dao/PolicyTest.java Mon Feb  4 18:22:00 2013
@@ -101,13 +101,23 @@ public class PolicyTest extends Abstract
     @Test
     public void create() {
         SyncPolicy policy = new SyncPolicy();
-        policy.setSpecification(new SyncPolicySpec());
+
+        final String syncURuleName = "net.tirasa.sync.correlation.TirasaURule";
+        final String syncRRuleName = "net.tirasa.sync.correlation.TirasaRRule";
+
+        SyncPolicySpec syncPolicySpec = new SyncPolicySpec();
+        syncPolicySpec.setUserJavaRule(syncURuleName);
+        syncPolicySpec.setRoleJavaRule(syncRRuleName);
+
+        policy.setSpecification(syncPolicySpec);
         policy.setDescription("Sync policy");
 
         policy = policyDAO.save(policy);
 
         assertNotNull(policy);
         assertEquals(PolicyType.SYNC, policy.getType());
+        assertEquals(syncURuleName, ((SyncPolicySpec) policy.getSpecification()).getUserJavaRule());
+        assertEquals(syncRRuleName, ((SyncPolicySpec) policy.getSpecification()).getRoleJavaRule());
     }
 
     @Test

Modified: syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/PolicyTestITCase.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/PolicyTestITCase.java?rev=1442286&r1=1442285&r2=1442286&view=diff
==============================================================================
--- syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/PolicyTestITCase.java (original)
+++ syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/PolicyTestITCase.java Mon Feb  4 18:22:00 2013
@@ -35,6 +35,7 @@ import org.apache.syncope.common.types.P
 import org.apache.syncope.common.types.SyncPolicySpec;
 import org.apache.syncope.common.types.SyncopeClientExceptionType;
 import org.apache.syncope.common.validation.SyncopeClientCompositeErrorException;
+import org.apache.syncope.core.sync.TestSyncRule;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
 import org.junit.runners.MethodSorters;
@@ -111,6 +112,7 @@ public class PolicyTestITCase extends Ab
 
         assertNotNull(policyTO);
         assertEquals(PolicyType.SYNC, policyTO.getType());
+        assertEquals(TestSyncRule.class.getName(), ((SyncPolicySpec) policyTO.getSpecification()).getUserJavaRule());
     }
 
     @Test
@@ -166,8 +168,13 @@ public class PolicyTestITCase extends Ab
 
     private SyncPolicyTO buildSyncPolicyTO() {
         SyncPolicyTO policy = new SyncPolicyTO();
-        policy.setSpecification(new SyncPolicySpec());
+
+        SyncPolicySpec spec = new SyncPolicySpec();
+        spec.setUserJavaRule(TestSyncRule.class.getName());
+
+        policy.setSpecification(spec);
         policy.setDescription("Sync policy");
+
         return policy;
     }
 }

Modified: syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java?rev=1442286&r1=1442285&r2=1442286&view=diff
==============================================================================
--- syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java (original)
+++ syncope/trunk/core/src/test/java/org/apache/syncope/core/rest/TaskTestITCase.java Mon Feb  4 18:22:00 2013
@@ -47,6 +47,7 @@ import org.apache.syncope.common.to.Sync
 import org.apache.syncope.common.to.TaskExecTO;
 import org.apache.syncope.common.to.TaskTO;
 import org.apache.syncope.common.to.UserTO;
+import org.apache.syncope.common.mod.UserMod;
 import org.apache.syncope.common.types.IntMappingType;
 import org.apache.syncope.common.types.PropagationTaskExecStatus;
 import org.apache.syncope.common.types.TaskType;
@@ -372,7 +373,8 @@ public class TaskTestITCase extends Abst
     }
 
     @Test
-    public void reconcileFromLDAP() throws InvalidSearchConditionException {
+    public void reconcileFromLDAP()
+            throws InvalidSearchConditionException {
         // Update sync task
         SyncTaskTO task = taskService.read(TaskType.SYNCHRONIZATION, 11L);
         assertNotNull(task);
@@ -777,6 +779,50 @@ public class TaskTestITCase extends Abst
         }
     }
 
+    @Test
+    public void issueSYNCOPE258() {
+        SyncTaskTO task = new SyncTaskTO();
+        task.setName("Test Sync Rule");
+        task.setResource("ws-target-resource-2");
+        task.setFullReconciliation(true);
+        task.setPerformCreate(true);
+        task.setPerformDelete(true);
+        task.setPerformUpdate(true);
+
+        Response response = taskService.create(task);
+        SyncTaskTO actual = getObject(response, SyncTaskTO.class, taskService);
+        assertNotNull(actual);
+
+        UserTO userTO = UserTestITCase.getUniqueSampleTO("s258_1@apache.org");
+        userTO.getResources().clear();
+        userTO.addResource("ws-target-resource-2");
+
+        userTO = createUser(userTO);
+
+        userTO = UserTestITCase.getUniqueSampleTO("s258_2@apache.org");
+        userTO.getResources().clear();
+        userTO.addResource("ws-target-resource-2");
+
+        userTO = createUser(userTO);
+
+        // change email in order to unmatch the second user
+        UserMod userMod = new UserMod();
+        userMod.setId(userTO.getId());
+        userMod.addAttributeToBeRemoved("email");
+        userMod.addAttributeToBeUpdated(attributeMod("email", "s258@apache.org"));
+
+        userTO = userService.update(userMod.getId(), userMod);
+        
+        execSyncTask(actual.getId(), 50, false);
+
+        SyncTaskTO executed = taskService.read(TaskType.SYNCHRONIZATION, actual.getId());
+        assertEquals(1, executed.getExecutions().size());
+
+        // asser for just one match
+        assertTrue(executed.getExecutions().get(0).getMessage().substring(0, 55) + "...",
+                executed.getExecutions().get(0).getMessage().contains("[updated/failures]: 1/0"));
+    }
+
     /**
      * remove initial and synchronized users to make test re-runnable
      */

Added: syncope/trunk/core/src/test/java/org/apache/syncope/core/sync/TestSyncRule.java
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/test/java/org/apache/syncope/core/sync/TestSyncRule.java?rev=1442286&view=auto
==============================================================================
--- syncope/trunk/core/src/test/java/org/apache/syncope/core/sync/TestSyncRule.java (added)
+++ syncope/trunk/core/src/test/java/org/apache/syncope/core/sync/TestSyncRule.java Mon Feb  4 18:22:00 2013
@@ -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.core.sync;
+
+import org.apache.syncope.common.search.AttributeCond;
+import org.apache.syncope.common.search.NodeCond;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+
+public class TestSyncRule implements SyncRule {
+
+    @Override
+    public NodeCond getSearchCond(ConnectorObject connObj) {
+        AttributeCond cond = new AttributeCond();
+        cond.setSchema("email");
+        cond.setType(AttributeCond.Type.EQ);
+        cond.setExpression(connObj.getName().getNameValue());
+
+        return NodeCond.getLeafCond(cond);
+    }
+}

Propchange: syncope/trunk/core/src/test/java/org/apache/syncope/core/sync/TestSyncRule.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: syncope/trunk/core/src/test/java/org/apache/syncope/core/sync/TestSyncRule.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: syncope/trunk/core/src/test/java/org/apache/syncope/core/sync/TestSyncRule.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: syncope/trunk/core/src/test/resources/content.xml
URL: http://svn.apache.org/viewvc/syncope/trunk/core/src/test/resources/content.xml?rev=1442286&r1=1442285&r2=1442286&view=diff
==============================================================================
--- syncope/trunk/core/src/test/resources/content.xml (original)
+++ syncope/trunk/core/src/test/resources/content.xml Mon Feb  4 18:22:00 2013
@@ -40,6 +40,7 @@ under the License.
   <Policy DTYPE="AccountPolicy" id="6" description="sample account policy" type="ACCOUNT" specification="%3Corg.apache.syncope.common.types.AccountPolicySpec%3E%0A++%3CmaxLength%3E0%3C%2FmaxLength%3E%0A++%3CminLength%3E4%3C%2FminLength%3E%0A++%3CprefixesNotPermitted%3E%0A++++%3Cstring%3Enotpermitted1%3C%2Fstring%3E%0A++++%3Cstring%3Enotpermitted2%3C%2Fstring%3E%0A++%3C%2FprefixesNotPermitted%3E%0A++%3CallUpperCase%3Efalse%3C%2FallUpperCase%3E%0A++%3CallLowerCase%3Efalse%3C%2FallLowerCase%3E%0A++%3CpropagateSuspension%3Efalse%3C%2FpropagateSuspension%3E%0A++%3CpermittedLoginRetries%3E3%3C%2FpermittedLoginRetries%3E%0A%3C%2Forg.apache.syncope.common.types.AccountPolicySpec%3E"/>
   <Policy DTYPE="SyncPolicy" id="7" description="sync policy 1" type="SYNC" specification="%3Corg.apache.syncope.common.types.SyncPolicySpec%2F%3E"/>
   <Policy DTYPE="PasswordPolicy" id="8" description="sample password policy" type="PASSWORD" specification="%3Corg.apache.syncope.common.types.PasswordPolicySpec%3E%0A++%3ChistoryLength%3E0%3C%2FhistoryLength%3E%0A++%3CmaxLength%3E0%3C%2FmaxLength%3E%0A++%3CminLength%3E10%3C%2FminLength%3E%0A++%3CnonAlphanumericRequired%3Etrue%3C%2FnonAlphanumericRequired%3E%0A++%3CalphanumericRequired%3Efalse%3C%2FalphanumericRequired%3E%0A++%3CdigitRequired%3Etrue%3C%2FdigitRequired%3E%0A++%3ClowercaseRequired%3Etrue%3C%2FlowercaseRequired%3E%0A++%3CuppercaseRequired%3Etrue%3C%2FuppercaseRequired%3E%0A++%3CmustStartWithDigit%3Etrue%3C%2FmustStartWithDigit%3E%0A++%3CmustntStartWithDigit%3Efalse%3C%2FmustntStartWithDigit%3E%0A++%3CmustEndWithDigit%3Etrue%3C%2FmustEndWithDigit%3E%0A++%3CmustntEndWithDigit%3Efalse%3C%2FmustntEndWithDigit%3E%0A++%3CmustStartWithNonAlpha%3Efalse%3C%2FmustStartWithNonAlpha%3E%0A++%3CmustStartWithAlpha%3Efalse%3C%2FmustStartWithAlpha%3E%0A++%3CmustntStartWithNonAl
 pha%3Efalse%3C%2FmustntStartWithNonAlpha%3E%0A++%3CmustntStartWithAlpha%3Efalse%3C%2FmustntStartWithAlpha%3E%0A++%3CmustEndWithNonAlpha%3Efalse%3C%2FmustEndWithNonAlpha%3E%0A++%3CmustEndWithAlpha%3Efalse%3C%2FmustEndWithAlpha%3E%0A++%3CmustntEndWithNonAlpha%3Efalse%3C%2FmustntEndWithNonAlpha%3E%0A++%3CmustntEndWithAlpha%3Efalse%3C%2FmustntEndWithAlpha%3E%0A++%3CprefixesNotPermitted%3E%0A++++%3Cstring%3Enotpermitted1%3C%2Fstring%3E%0A++++%3Cstring%3Enotpermitted2%3C%2Fstring%3E%0A++%3C%2FprefixesNotPermitted%3E%0A%3C%2Forg.apache.syncope.common.types.PasswordPolicySpec%3E"/>
+  <Policy DTYPE="SyncPolicy" id="9" description="sync policy with a rule" type="SYNC" specification="%3Corg.apache.syncope.common.types.SyncPolicySpec%3E%0A++%3CuserJavaRule%3Eorg.apache.syncope.core.sync.TestSyncRule%3C/userJavaRule%3E%0A++%3CroleJavaRule%3E%3C/roleJavaRule%3E%0A++%3CconflictResolutionAction%3EIGNORE%3C/conflictResolutionAction%3E%0A%3C/org.apache.syncope.common.types.SyncPolicySpec%3E"/>
     
   <SyncopeUser id="1" workflowId="0" status="active" password="5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8" cipherAlgorithm="SHA1"
                username="user1" creationDate="2010-10-20 11:00:00" suspended="0"/>
@@ -343,7 +344,8 @@ under the License.
                     propagationPriority="0" propagationPrimary="1" createTraceLevel="ALL" deleteTraceLevel="ALL" updateTraceLevel="ALL" syncTraceLevel="ALL"/>
   <ExternalResource name="ws-target-resource-2" connector_id="100"
                     randomPwdIfNotProvided="0" enforceMandatoryCondition="1" propagationMode="TWO_PHASES"
-                    propagationPriority="0" propagationPrimary="1" createTraceLevel="FAILURES" deleteTraceLevel="NONE" updateTraceLevel="ALL" syncTraceLevel="ALL"/>
+                    propagationPriority="0" propagationPrimary="1" createTraceLevel="FAILURES" deleteTraceLevel="NONE" updateTraceLevel="ALL" syncTraceLevel="ALL"
+                    syncPolicy_id="9"/>
   <ExternalResource name="ws-target-resource-3" connector_id="102"
                     randomPwdIfNotProvided="0" enforceMandatoryCondition="1" propagationMode="TWO_PHASES"
                     propagationPriority="0" propagationPrimary="1" createTraceLevel="FAILURES" deleteTraceLevel="NONE" updateTraceLevel="ALL" syncTraceLevel="ALL"