syncope-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From fmarte...@apache.org
Subject [23/26] syncope git commit: [SYNCOPE-665] Implementation completed
Date Thu, 10 Sep 2015 12:37:53 GMT
[SYNCOPE-665] Implementation completed


Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/00988759
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/00988759
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/00988759

Branch: refs/heads/SYNCOPE-156
Commit: 00988759750f53f6737ef7d50e7faf63192257fa
Parents: 9ab997b
Author: Francesco Chicchiriccò <ilgrosso@apache.org>
Authored: Tue Sep 8 14:12:34 2015 +0200
Committer: Francesco Chicchiriccò <ilgrosso@apache.org>
Committed: Tue Sep 8 14:12:34 2015 +0200

----------------------------------------------------------------------
 .../client/console/panels/ConnectorModal.java   |  10 +-
 .../client/console/panels/ResourceModal.java    |   2 -
 .../console/topology/WebSocketBehavior.java     |  29 ++-
 .../apache/syncope/common/lib/to/RealmTO.java   |  28 +++
 .../apache/syncope/common/lib/to/SyncopeTO.java |  19 +-
 .../common/lib/types/EntityViolationType.java   |   2 +-
 .../syncope/core/logic/AbstractAnyLogic.java    | 150 +++++++++++++
 .../syncope/core/logic/AnyObjectLogic.java      |  68 +++---
 .../apache/syncope/core/logic/GroupLogic.java   |  68 +++---
 .../apache/syncope/core/logic/SyncopeLogic.java |   7 +-
 .../apache/syncope/core/logic/UserLogic.java    | 120 +++++-----
 .../init/ClassPathScanImplementationLookup.java |  16 +-
 core/logic/src/main/resources/logic.properties  |   1 -
 core/logic/src/main/resources/logicContext.xml  |   1 -
 .../syncope/core/misc/ConnObjectUtils.java      | 139 +-----------
 .../apache/syncope/core/misc/TemplateUtils.java | 223 +++++++++++++++++++
 .../persistence/api/ImplementationLookup.java   |   1 +
 .../persistence/api/entity/AnyTemplate.java     |  32 +++
 .../api/entity/AnyTemplateRealm.java            |  26 +++
 .../core/persistence/api/entity/Realm.java      |  13 +-
 .../api/entity/task/AnyTemplate.java            |  38 ----
 .../api/entity/task/AnyTemplateSyncTask.java    |  28 +++
 .../persistence/api/entity/task/SyncTask.java   |   8 +-
 .../core/persistence/jpa/dao/JPAAnyTypeDAO.java |   4 +
 .../core/persistence/jpa/dao/JPARealmDAO.java   |   2 +
 .../jpa/entity/JPAAnyTemplateRealm.java         |  61 +++++
 .../jpa/entity/JPAEntityFactory.java            |  11 +-
 .../core/persistence/jpa/entity/JPARealm.java   |  57 +++++
 .../entity/resource/AbstractAnyTemplate.java    |  74 ++++++
 .../jpa/entity/task/JPAAnyTemplate.java         | 104 ---------
 .../jpa/entity/task/JPAAnyTemplateSyncTask.java |  61 +++++
 .../jpa/entity/task/JPASyncTask.java            |  21 +-
 .../entity/ProvisioningTaskValidator.java       |   7 +-
 .../jpa/validation/entity/RealmValidator.java   |  36 ++-
 .../resources/META-INF/spring-orm-oracle.xml    |  15 +-
 .../resources/META-INF/spring-orm-sqlserver.xml |  15 +-
 .../src/main/resources/META-INF/spring-orm.xml  |  15 +-
 .../core/persistence/jpa/outer/TaskTest.java    |   5 +-
 .../test/resources/domains/MasterContent.xml    |  32 +--
 .../core/provisioning/api/AnyTransformer.java   |  33 ---
 .../core/provisioning/api/LogicActions.java     |  40 ++++
 .../core/provisioning/api/sync/PushActions.java |  52 +++--
 .../core/provisioning/api/sync/SyncActions.java |  54 +++--
 .../java/DefaultAnyTransformer.java             |  40 ----
 .../provisioning/java/DefaultLogicActions.java  |  57 +++++
 .../java/data/AnyObjectDataBinderImpl.java      |   8 +-
 .../java/data/GroupDataBinderImpl.java          |   2 +-
 .../java/data/RealmDataBinderImpl.java          |  63 ++++++
 .../java/data/TaskDataBinderImpl.java           |  61 +----
 .../java/data/UserDataBinderImpl.java           |   8 +-
 .../java/sync/AbstractSyncResultHandler.java    |  30 +--
 .../java/sync/DBPasswordSyncActions.java        |  14 +-
 .../java/sync/DefaultPushActions.java           |  20 +-
 .../java/sync/DefaultSyncActions.java           |  38 ++--
 .../java/sync/LDAPMembershipSyncActions.java    |   8 +-
 .../java/sync/LDAPPasswordSyncActions.java      |  14 +-
 .../provisioning/java/sync/SyncJobDelegate.java |   2 +-
 .../reference/DoubleValueAnyTransformer.java    |  75 -------
 .../core/reference/DoubleValueLogicActions.java |  75 +++++++
 .../fit/core/reference/TestSyncActions.java     |  14 +-
 .../src/main/resources/logic.properties         |   1 -
 .../syncope/fit/core/reference/UserITCase.java  |  12 +-
 .../installer/processes/BaseProcess.java        |   2 -
 63 files changed, 1416 insertions(+), 856 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/00988759/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnectorModal.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnectorModal.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnectorModal.java
index 9db9bd2..4f5e8eb 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnectorModal.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ConnectorModal.java
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.client.console.panels;
 
-import static org.apache.wicket.Component.ENABLE;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -317,8 +315,8 @@ public class ConnectorModal extends AbstractResourceModal {
         connectorPropForm.add(check);
 
         // form - third tab (capabilities)
-        final IModel<List<ConnectorCapability>> capabilities
-                = new LoadableDetachableModel<List<ConnectorCapability>>() {
+        final IModel<List<ConnectorCapability>> capabilities =
+                 new LoadableDetachableModel<List<ConnectorCapability>>() {
 
                     private static final long serialVersionUID = 5275935387613157437L;
 
@@ -327,8 +325,8 @@ public class ConnectorModal extends AbstractResourceModal {
                         return Arrays.asList(ConnectorCapability.values());
                     }
                 };
-        CheckBoxMultipleChoiceFieldPanel<ConnectorCapability> capabilitiesPalette
-                = new CheckBoxMultipleChoiceFieldPanel<>(
+        CheckBoxMultipleChoiceFieldPanel<ConnectorCapability> capabilitiesPalette =
+                 new CheckBoxMultipleChoiceFieldPanel<>(
                         "capabilitiesPalette",
                         new PropertyModel<List<ConnectorCapability>>(this, "selectedCapabilities"), capabilities);
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/00988759/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceModal.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceModal.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceModal.java
index 6709bb8..54b22b8 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceModal.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceModal.java
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.client.console.panels;
 
-import static org.apache.wicket.Component.ENABLE;
-
 import java.util.ArrayList;
 import java.util.Collection;
 import org.apache.commons.collections4.CollectionUtils;

http://git-wip-us.apache.org/repos/asf/syncope/blob/00988759/client/console/src/main/java/org/apache/syncope/client/console/topology/WebSocketBehavior.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/topology/WebSocketBehavior.java b/client/console/src/main/java/org/apache/syncope/client/console/topology/WebSocketBehavior.java
index 7cd0ff5..1b847f9 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/topology/WebSocketBehavior.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/topology/WebSocketBehavior.java
@@ -18,11 +18,6 @@
  */
 package org.apache.syncope.client.console.topology;
 
-import static org.apache.syncope.client.console.topology.TopologyNode.Status.FAILURE;
-import static org.apache.syncope.client.console.topology.TopologyNode.Status.REACHABLE;
-import static org.apache.syncope.client.console.topology.TopologyNode.Status.UNKNOWN;
-import static org.apache.syncope.client.console.topology.TopologyNode.Status.UNREACHABLE;
-
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import java.io.IOException;
@@ -46,15 +41,15 @@ import org.slf4j.LoggerFactory;
 
 public class WebSocketBehavior extends org.apache.wicket.protocol.ws.api.WebSocketBehavior {
 
-    private static final long serialVersionUID = 1L;
-
     protected static final Logger LOG = LoggerFactory.getLogger(WebSocketBehavior.class);
 
-    private final Map<String, String> resources = new HashMap<String, String>();
+    private static final long serialVersionUID = -1653665542635275551L;
+
+    private final Map<String, String> resources = new HashMap<>();
 
     private final Set<String> runningResCheck = new HashSet<>();
 
-    private final Map<Long, String> connectors = new HashMap<Long, String>();
+    private final Map<Long, String> connectors = new HashMap<>();
 
     private final Set<Long> runningConnCheck = new HashSet<>();
 
@@ -77,7 +72,8 @@ public class WebSocketBehavior extends org.apache.wicket.protocol.ws.api.WebSock
                     if (connectors.containsKey(ckey)) {
                         handler.push(connectors.get(ckey));
                     } else {
-                        handler.push(String.format("{ \"status\": \"%s\", \"target\": \"%s\"}", UNKNOWN, ckey));
+                        handler.push(String.format(
+                                "{ \"status\": \"%s\", \"target\": \"%s\"}", TopologyNode.Status.UNKNOWN, ckey));
                     }
 
                     synchronized (runningConnCheck) {
@@ -97,7 +93,8 @@ public class WebSocketBehavior extends org.apache.wicket.protocol.ws.api.WebSock
                     if (resources.containsKey(rkey)) {
                         handler.push(resources.get(rkey));
                     } else {
-                        handler.push(String.format("{ \"status\": \"%s\", \"target\": \"%s\"}", UNKNOWN, rkey));
+                        handler.push(String.format(
+                                "{ \"status\": \"%s\", \"target\": \"%s\"}", TopologyNode.Status.UNKNOWN, rkey));
                     }
 
                     synchronized (runningResCheck) {
@@ -151,10 +148,11 @@ public class WebSocketBehavior extends org.apache.wicket.protocol.ws.api.WebSock
                 try {
                     final ConnInstanceTO connector = connectorRestClient.read(key);
                     res = String.format("{ \"status\": \"%s\", \"target\": \"%s\"}",
-                            connectorRestClient.check(connector) ? REACHABLE : UNREACHABLE, key);
+                            connectorRestClient.check(connector)
+                                    ? TopologyNode.Status.REACHABLE : TopologyNode.Status.UNREACHABLE, key);
                 } catch (Exception e) {
                     LOG.warn("Error checking connection for {}", key, e);
-                    res = String.format("{ \"status\": \"%s\", \"target\": \"%s\"}", FAILURE, key);
+                    res = String.format("{ \"status\": \"%s\", \"target\": \"%s\"}", TopologyNode.Status.FAILURE, key);
                 }
 
                 synchronized (runningConnCheck) {
@@ -191,10 +189,11 @@ public class WebSocketBehavior extends org.apache.wicket.protocol.ws.api.WebSock
                 try {
                     final ResourceTO resource = resourceRestClient.read(key);
                     res = String.format("{ \"status\": \"%s\", \"target\": \"%s\"}",
-                            connectorRestClient.check(resource) ? REACHABLE : UNREACHABLE, key);
+                            connectorRestClient.check(resource)
+                                    ? TopologyNode.Status.REACHABLE : TopologyNode.Status.UNREACHABLE, key);
                 } catch (Exception e) {
                     LOG.warn("Error checking connection for {}", key, e);
-                    res = String.format("{ \"status\": \"%s\", \"target\": \"%s\"}", FAILURE, key);
+                    res = String.format("{ \"status\": \"%s\", \"target\": \"%s\"}", TopologyNode.Status.FAILURE, key);
                 }
 
                 synchronized (runningResCheck) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/00988759/common/lib/src/main/java/org/apache/syncope/common/lib/to/RealmTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/RealmTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/RealmTO.java
index faef389..0c03798 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/RealmTO.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/RealmTO.java
@@ -18,10 +18,20 @@
  */
 package org.apache.syncope.common.lib.to;
 
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
 import javax.ws.rs.PathParam;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
 import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 import org.apache.syncope.common.lib.AbstractBaseBean;
+import org.apache.syncope.common.lib.jaxb.XmlGenericMapAdapter;
 
 @XmlRootElement(name = "realm")
 @XmlType
@@ -41,6 +51,12 @@ public class RealmTO extends AbstractBaseBean {
 
     private Long passwordPolicy;
 
+    private final Set<String> actionsClassNames = new HashSet<>();
+
+    @XmlJavaTypeAdapter(XmlGenericMapAdapter.class)
+    @JsonIgnore
+    private final Map<String, AnyTO> templates = new HashMap<>();
+
     public long getKey() {
         return key;
     }
@@ -90,4 +106,16 @@ public class RealmTO extends AbstractBaseBean {
         this.passwordPolicy = passwordPolicy;
     }
 
+    @XmlElementWrapper(name = "actionsClassNames")
+    @XmlElement(name = "actionsClassName")
+    @JsonProperty("actionsClassNames")
+    public Set<String> getActionsClassNames() {
+        return actionsClassNames;
+    }
+
+    @JsonProperty
+    public Map<String, AnyTO> getTemplates() {
+        return templates;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/00988759/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncopeTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncopeTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncopeTO.java
index fe57d52..638f32f 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncopeTO.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncopeTO.java
@@ -43,8 +43,6 @@ public class SyncopeTO extends AbstractBaseBean {
 
     private final List<String> connIdLocations = new ArrayList<>();
 
-    private String anyTransformer;
-
     private String anyObjectWorkflowAdapter;
 
     private String userWorkflowAdapter;
@@ -69,6 +67,8 @@ public class SyncopeTO extends AbstractBaseBean {
 
     private final List<String> taskJobs = new ArrayList<>();
 
+    private final List<String> logicActions = new ArrayList<>();
+
     private final List<String> propagationActions = new ArrayList<>();
 
     private final List<String> syncActions = new ArrayList<>();
@@ -106,10 +106,6 @@ public class SyncopeTO extends AbstractBaseBean {
         return connIdLocations;
     }
 
-    public String getAnyTransformer() {
-        return anyTransformer;
-    }
-
     public String getAnyObjectWorkflowAdapter() {
         return anyObjectWorkflowAdapter;
     }
@@ -174,6 +170,13 @@ public class SyncopeTO extends AbstractBaseBean {
         return taskJobs;
     }
 
+    @XmlElementWrapper(name = "logicActions")
+    @XmlElement(name = "logicAction")
+    @JsonProperty("logicActions")
+    public List<String> getLogicActions() {
+        return logicActions;
+    }
+
     @XmlElementWrapper(name = "propagationActions")
     @XmlElement(name = "propagationAction")
     @JsonProperty("propagationActions")
@@ -239,10 +242,6 @@ public class SyncopeTO extends AbstractBaseBean {
         this.pwdResetRequiringSecurityQuestions = pwdResetRequiringSecurityQuestions;
     }
 
-    public void setAnyTransformer(final String anyTransformer) {
-        this.anyTransformer = anyTransformer;
-    }
-
     public void setAnyObjectWorkflowAdapter(final String anyObjectWorkflowAdapter) {
         this.anyObjectWorkflowAdapter = anyObjectWorkflowAdapter;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/00988759/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java
index a97f510..8324525 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java
@@ -41,7 +41,7 @@ public enum EntityViolationType {
     InvalidSchemaEnum("org.apache.syncope.core.persistence.validation.schema.enum"),
     InvalidSchemaMultivalueUnique("org.apache.syncope.core.persistence.validation.schema.multivalueUnique"),
     InvalidSchedTask("org.apache.syncope.core.persistence.validation.schedtask"),
-    InvalidSyncTask("org.apache.syncope.core.persistence.validation.synctask"),
+    InvalidProvisioningTask("org.apache.syncope.core.persistence.validation.synctask"),
     InvalidPlainSchema("org.apache.syncope.core.persistence.validation.attrvalue.plainSchema"),
     InvalidDerSchema("org.apache.syncope.core.persistence.validation.attrvalue.derSchema"),
     InvalidVirSchema("org.apache.syncope.core.persistence.validation.attrvalue.virSchema"),

http://git-wip-us.apache.org/repos/asf/syncope/blob/00988759/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java
index ed2a865..cd40a6b 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java
@@ -18,23 +18,173 @@
  */
 package org.apache.syncope.core.logic;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Predicate;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.mod.AnyMod;
 import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.GroupTO;
+import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.core.misc.RealmUtils;
+import org.apache.syncope.core.misc.TemplateUtils;
 import org.apache.syncope.core.misc.security.DelegatedAdministrationException;
+import org.apache.syncope.core.misc.spring.ApplicationContextProvider;
+import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
+import org.apache.syncope.core.persistence.api.dao.RealmDAO;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.api.entity.AnyType;
+import org.apache.syncope.core.persistence.api.entity.Realm;
+import org.apache.syncope.core.provisioning.api.LogicActions;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
 
 public abstract class AbstractAnyLogic<TO extends AnyTO, MOD extends AnyMod>
         extends AbstractResourceAssociator<TO> {
 
+    @Autowired
+    private RealmDAO realmDAO;
+
+    @Autowired
+    private AnyTypeDAO anyTypeDAO;
+
+    @Autowired
+    private TemplateUtils templateUtils;
+
+    private List<LogicActions> getActions(final Realm realm) {
+        List<LogicActions> actions = new ArrayList<>();
+
+        for (String className : realm.getActionsClassNames()) {
+            try {
+                Class<?> actionsClass = Class.forName(className);
+                LogicActions logicActions = (LogicActions) ApplicationContextProvider.getBeanFactory().
+                        createBean(actionsClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, true);
+
+                actions.add(logicActions);
+            } catch (Exception e) {
+                LOG.warn("Class '{}' not found", className, e);
+            }
+        }
+
+        return actions;
+    }
+
+    protected Pair<TO, List<LogicActions>> beforeCreate(final TO input) {
+        Realm realm = realmDAO.find(input.getRealm());
+        if (realm == null) {
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRealm);
+            sce.getElements().add(input.getRealm());
+            throw sce;
+        }
+
+        AnyType anyType = input instanceof UserTO
+                ? anyTypeDAO.findUser()
+                : input instanceof GroupTO
+                        ? anyTypeDAO.findGroup()
+                        : anyTypeDAO.find(input.getType());
+        if (anyType == null) {
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidAnyType);
+            sce.getElements().add(input.getType());
+            throw sce;
+        }
+
+        TO any = input;
+
+        templateUtils.apply(any, realm.getTemplate(anyType));
+
+        List<LogicActions> actions = getActions(realm);
+        for (LogicActions action : actions) {
+            any = action.beforeCreate(any);
+        }
+
+        LOG.debug("Input: {}\nOutput: {}\n", input, any);
+
+        return ImmutablePair.of(any, actions);
+    }
+
+    protected TO afterCreate(final TO input, final List<LogicActions> actions) {
+        TO any = input;
+
+        for (LogicActions action : actions) {
+            any = action.afterCreate(any);
+        }
+
+        return any;
+    }
+
+    protected Pair<MOD, List<LogicActions>> beforeUpdate(final MOD input, final String realmPath) {
+        if (StringUtils.isBlank(input.getRealm())) {
+            input.setRealm(realmPath);
+        }
+        Realm realm = realmDAO.find(input.getRealm());
+        if (realm == null) {
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRealm);
+            sce.getElements().add(input.getRealm());
+            throw sce;
+        }
+
+        MOD mod = input;
+
+        List<LogicActions> actions = getActions(realm);
+        for (LogicActions action : actions) {
+            mod = action.beforeUpdate(mod);
+        }
+
+        LOG.debug("Input: {}\nOutput: {}\n", input, mod);
+
+        return ImmutablePair.of(mod, actions);
+    }
+
+    protected TO afterUpdate(final TO input, final List<LogicActions> actions) {
+        TO any = input;
+
+        for (LogicActions action : actions) {
+            any = action.afterUpdate(any);
+        }
+
+        return any;
+    }
+
+    protected Pair<TO, List<LogicActions>> beforeDelete(final TO input) {
+        Realm realm = realmDAO.find(input.getRealm());
+        if (realm == null) {
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidRealm);
+            sce.getElements().add(input.getRealm());
+            throw sce;
+        }
+
+        TO any = input;
+
+        List<LogicActions> actions = getActions(realm);
+        for (LogicActions action : actions) {
+            any = action.beforeDelete(any);
+        }
+
+        LOG.debug("Input: {}\nOutput: {}\n", input, any);
+
+        return ImmutablePair.of(any, actions);
+    }
+
+    protected TO afterDelete(final TO input, final List<LogicActions> actions) {
+        TO any = input;
+
+        for (LogicActions action : actions) {
+            any = action.afterDelete(any);
+        }
+
+        return any;
+    }
+
     private static class StartsWithPredicate implements Predicate<String> {
 
         private final Collection<String> targets;

http://git-wip-us.apache.org/repos/asf/syncope/blob/00988759/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
index 0b0cb74..5e52b9e 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
@@ -31,6 +31,7 @@ import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Transformer;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.mod.AnyObjectMod;
@@ -49,7 +50,7 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecu
 import org.apache.syncope.core.misc.security.AuthContextUtils;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
-import org.apache.syncope.core.provisioning.api.AnyTransformer;
+import org.apache.syncope.core.provisioning.api.LogicActions;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Component;
@@ -77,9 +78,6 @@ public class AnyObjectLogic extends AbstractAnyLogic<AnyObjectTO, AnyObjectMod>
     @Autowired
     protected PropagationTaskExecutor taskExecutor;
 
-    @Autowired
-    protected AnyTransformer attrTransformer;
-
     @Resource(name = "anonymousUser")
     protected String anonymousUser;
 
@@ -160,79 +158,73 @@ public class AnyObjectLogic extends AbstractAnyLogic<AnyObjectTO, AnyObjectMod>
     @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_CREATE + "')")
     @Override
     public AnyObjectTO create(final AnyObjectTO anyObjectTO) {
-        if (anyObjectTO.getRealm() == null) {
+        Pair<AnyObjectTO, List<LogicActions>> before = beforeCreate(anyObjectTO);
+
+        if (before.getLeft().getRealm() == null) {
             throw SyncopeClientException.build(ClientExceptionType.InvalidRealm);
         }
-        // security checks
+
         Set<String> effectiveRealms = getEffectiveRealms(
                 AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_CREATE),
-                Collections.singleton(anyObjectTO.getRealm()));
-        securityChecks(effectiveRealms, anyObjectTO.getRealm(), null);
-
-        // Any transformation (if configured)
-        AnyObjectTO actual = attrTransformer.transform(anyObjectTO);
-        LOG.debug("Transformed: {}", actual);
+                Collections.singleton(before.getLeft().getRealm()));
+        securityChecks(effectiveRealms, before.getLeft().getRealm(), null);
 
-        if (anyObjectTO.getType() == null) {
+        if (before.getLeft().getType() == null) {
             throw SyncopeClientException.build(ClientExceptionType.InvalidAnyType);
         }
 
-        /*
-         * Actual operations: workflow, propagation
-         */
-        Map.Entry<Long, List<PropagationStatus>> created = provisioningManager.create(anyObjectTO);
+        Map.Entry<Long, List<PropagationStatus>> created = provisioningManager.create(before.getLeft());
         AnyObjectTO savedTO = binder.getAnyObjectTO(created.getKey());
         savedTO.getPropagationStatusTOs().addAll(created.getValue());
-        return savedTO;
+
+        return afterCreate(savedTO, before.getValue());
     }
 
     @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_UPDATE + "')")
     @Override
     public AnyObjectTO update(final AnyObjectMod anyObjectMod) {
-        // Any transformation (if configured)
-        AnyObjectMod actual = attrTransformer.transform(anyObjectMod);
-        LOG.debug("Transformed: {}", actual);
+        AnyObjectTO anyObjectTO = binder.getAnyObjectTO(anyObjectMod.getKey());
+        Pair<AnyObjectMod, List<LogicActions>> before = beforeUpdate(anyObjectMod, anyObjectTO.getRealm());
 
-        // security checks
-        AnyObjectTO toUpdate = binder.getAnyObjectTO(anyObjectMod.getKey());
         Set<String> requestedRealms = new HashSet<>();
-        requestedRealms.add(toUpdate.getRealm());
-        if (StringUtils.isNotBlank(actual.getRealm())) {
-            requestedRealms.add(actual.getRealm());
+        requestedRealms.add(before.getLeft().getRealm());
+        if (StringUtils.isNotBlank(before.getLeft().getRealm())) {
+            requestedRealms.add(before.getLeft().getRealm());
         }
         Set<String> effectiveRealms = getEffectiveRealms(
                 AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_UPDATE),
                 requestedRealms);
-        securityChecks(effectiveRealms, toUpdate.getRealm(), toUpdate.getKey());
-        if (StringUtils.isNotBlank(actual.getRealm())) {
-            securityChecks(effectiveRealms, actual.getRealm(), toUpdate.getKey());
+        securityChecks(effectiveRealms, before.getLeft().getRealm(), before.getLeft().getKey());
+        if (StringUtils.isNotBlank(before.getLeft().getRealm())) {
+            securityChecks(effectiveRealms, before.getLeft().getRealm(), before.getLeft().getKey());
         }
 
         Map.Entry<Long, List<PropagationStatus>> updated = provisioningManager.update(anyObjectMod);
 
         AnyObjectTO updatedTO = binder.getAnyObjectTO(updated.getKey());
         updatedTO.getPropagationStatusTOs().addAll(updated.getValue());
-        return updatedTO;
+
+        return afterUpdate(updatedTO, before.getRight());
     }
 
     @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_DELETE + "')")
     @Override
     public AnyObjectTO delete(final Long key) {
-        // security checks
-        AnyObjectTO toDelete = binder.getAnyObjectTO(key);
+        AnyObjectTO anyObject = binder.getAnyObjectTO(key);
+        Pair<AnyObjectTO, List<LogicActions>> before = beforeDelete(anyObject);
+
         Set<String> effectiveRealms = getEffectiveRealms(
                 AuthContextUtils.getAuthorizations().get(Entitlement.ANY_OBJECT_DELETE),
-                Collections.singleton(toDelete.getRealm()));
-        securityChecks(effectiveRealms, toDelete.getRealm(), toDelete.getKey());
+                Collections.singleton(before.getLeft().getRealm()));
+        securityChecks(effectiveRealms, before.getLeft().getRealm(), before.getLeft().getKey());
 
-        List<PropagationStatus> statuses = provisioningManager.delete(key);
+        List<PropagationStatus> statuses = provisioningManager.delete(before.getLeft().getKey());
 
         AnyObjectTO anyObjectTO = new AnyObjectTO();
-        anyObjectTO.setKey(key);
-
+        anyObjectTO.setKey(before.getLeft().getKey());
         anyObjectTO.getPropagationStatusTOs().addAll(statuses);
 
-        return anyObjectTO;
+        return afterDelete(anyObjectTO, before.getRight());
     }
 
     @PreAuthorize("hasRole('" + Entitlement.ANY_OBJECT_UPDATE + "')")

http://git-wip-us.apache.org/repos/asf/syncope/blob/00988759/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
index 5f7cf6f..a8af2b9 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
@@ -32,6 +32,7 @@ import org.apache.commons.collections4.Predicate;
 import org.apache.commons.collections4.Transformer;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.mod.GroupMod;
@@ -53,7 +54,7 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecu
 import org.apache.syncope.core.misc.security.AuthContextUtils;
 import org.apache.syncope.core.misc.security.DelegatedAdministrationException;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
-import org.apache.syncope.core.provisioning.api.AnyTransformer;
+import org.apache.syncope.core.provisioning.api.LogicActions;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.stereotype.Component;
@@ -84,9 +85,6 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupMod> {
     @Autowired
     protected PropagationTaskExecutor taskExecutor;
 
-    @Autowired
-    protected AnyTransformer attrTransformer;
-
     @Resource(name = "anonymousUser")
     protected String anonymousUser;
 
@@ -184,68 +182,63 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupMod> {
     @PreAuthorize("hasRole('" + Entitlement.GROUP_CREATE + "')")
     @Override
     public GroupTO create(final GroupTO groupTO) {
-        if (groupTO.getRealm() == null) {
+        Pair<GroupTO, List<LogicActions>> before = beforeCreate(groupTO);
+
+        if (before.getLeft().getRealm() == null) {
             throw SyncopeClientException.build(ClientExceptionType.InvalidRealm);
         }
-        // security checks
+
         Set<String> effectiveRealms = getEffectiveRealms(
                 AuthContextUtils.getAuthorizations().get(Entitlement.GROUP_CREATE),
-                Collections.singleton(groupTO.getRealm()));
-        securityChecks(effectiveRealms, groupTO.getRealm(), null);
-
-        // Any transformation (if configured)
-        GroupTO actual = attrTransformer.transform(groupTO);
-        LOG.debug("Transformed: {}", actual);
+                Collections.singleton(before.getLeft().getRealm()));
+        securityChecks(effectiveRealms, before.getLeft().getRealm(), null);
 
-        /*
-         * Actual operations: workflow, propagation
-         */
-        Map.Entry<Long, List<PropagationStatus>> created = provisioningManager.create(groupTO);
+        Map.Entry<Long, List<PropagationStatus>> created = provisioningManager.create(before.getLeft());
         GroupTO savedTO = binder.getGroupTO(created.getKey());
         savedTO.getPropagationStatusTOs().addAll(created.getValue());
-        return savedTO;
+
+        return afterCreate(savedTO, before.getValue());
     }
 
     @PreAuthorize("hasRole('" + Entitlement.GROUP_UPDATE + "')")
     @Override
     public GroupTO update(final GroupMod groupMod) {
-        // Any transformation (if configured)
-        GroupMod actual = attrTransformer.transform(groupMod);
-        LOG.debug("Transformed: {}", actual);
+        GroupTO groupTO = binder.getGroupTO(groupMod.getKey());
+        Pair<GroupMod, List<LogicActions>> before = beforeUpdate(groupMod, groupTO.getRealm());
 
-        // security checks
-        GroupTO toUpdate = binder.getGroupTO(groupMod.getKey());
         Set<String> requestedRealms = new HashSet<>();
-        requestedRealms.add(toUpdate.getRealm());
-        if (StringUtils.isNotBlank(actual.getRealm())) {
-            requestedRealms.add(actual.getRealm());
+        requestedRealms.add(before.getLeft().getRealm());
+        if (StringUtils.isNotBlank(before.getLeft().getRealm())) {
+            requestedRealms.add(before.getLeft().getRealm());
         }
         Set<String> effectiveRealms = getEffectiveRealms(
                 AuthContextUtils.getAuthorizations().get(Entitlement.GROUP_UPDATE),
                 requestedRealms);
-        securityChecks(effectiveRealms, toUpdate.getRealm(), toUpdate.getKey());
-        if (StringUtils.isNotBlank(actual.getRealm())) {
-            securityChecks(effectiveRealms, actual.getRealm(), toUpdate.getKey());
+        securityChecks(effectiveRealms, before.getLeft().getRealm(), before.getLeft().getKey());
+        if (StringUtils.isNotBlank(before.getLeft().getRealm())) {
+            securityChecks(effectiveRealms, before.getLeft().getRealm(), before.getLeft().getKey());
         }
 
         Map.Entry<Long, List<PropagationStatus>> updated = provisioningManager.update(groupMod);
 
         GroupTO updatedTO = binder.getGroupTO(updated.getKey());
         updatedTO.getPropagationStatusTOs().addAll(updated.getValue());
-        return updatedTO;
+
+        return afterUpdate(updatedTO, before.getRight());
     }
 
     @PreAuthorize("hasRole('" + Entitlement.GROUP_DELETE + "')")
     @Override
     public GroupTO delete(final Long key) {
-        // security checks
-        GroupTO toDelete = binder.getGroupTO(key);
+        GroupTO group = binder.getGroupTO(key);
+        Pair<GroupTO, List<LogicActions>> before = beforeDelete(group);
+
         Set<String> effectiveRealms = getEffectiveRealms(
                 AuthContextUtils.getAuthorizations().get(Entitlement.GROUP_DELETE),
-                Collections.singleton(toDelete.getRealm()));
-        securityChecks(effectiveRealms, toDelete.getRealm(), toDelete.getKey());
+                Collections.singleton(before.getLeft().getRealm()));
+        securityChecks(effectiveRealms, before.getLeft().getRealm(), before.getLeft().getKey());
 
-        List<Group> ownedGroups = groupDAO.findOwnedByGroup(key);
+        List<Group> ownedGroups = groupDAO.findOwnedByGroup(before.getLeft().getKey());
         if (!ownedGroups.isEmpty()) {
             SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.GroupOwnership);
             sce.getElements().addAll(CollectionUtils.collect(ownedGroups, new Transformer<Group, String>() {
@@ -258,14 +251,13 @@ public class GroupLogic extends AbstractAnyLogic<GroupTO, GroupMod> {
             throw sce;
         }
 
-        List<PropagationStatus> statuses = provisioningManager.delete(key);
+        List<PropagationStatus> statuses = provisioningManager.delete(before.getLeft().getKey());
 
         GroupTO groupTO = new GroupTO();
-        groupTO.setKey(key);
-
+        groupTO.setKey(before.getLeft().getKey());
         groupTO.getPropagationStatusTOs().addAll(statuses);
 
-        return groupTO;
+        return afterDelete(groupTO, before.getRight());
     }
 
     @PreAuthorize("hasRole('" + Entitlement.GROUP_UPDATE + "')")

http://git-wip-us.apache.org/repos/asf/syncope/blob/00988759/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
index 1354f67..7f9b7c9 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
@@ -31,7 +31,6 @@ import org.apache.syncope.core.persistence.api.ImplementationLookup;
 import org.apache.syncope.core.persistence.api.ImplementationLookup.Type;
 import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager;
-import org.apache.syncope.core.provisioning.api.AnyTransformer;
 import org.apache.syncope.core.provisioning.api.ConnIdBundleManager;
 import org.apache.syncope.core.provisioning.api.GroupProvisioningManager;
 import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
@@ -59,9 +58,6 @@ public class SyncopeLogic extends AbstractLogic<SyncopeTO> {
     private ConnIdBundleManager bundleManager;
 
     @Autowired
-    private AnyTransformer anyTransformer;
-
-    @Autowired
     private AnyObjectWorkflowAdapter awfAdapter;
 
     @Autowired
@@ -122,8 +118,6 @@ public class SyncopeLogic extends AbstractLogic<SyncopeTO> {
             }
         }
 
-        syncopeTO.setAnyTransformer(anyTransformer.getClass().getName());
-
         syncopeTO.setAnyObjectWorkflowAdapter(AopUtils.getTargetClass(awfAdapter).getName());
         syncopeTO.setUserWorkflowAdapter(AopUtils.getTargetClass(uwfAdapter).getName());
         syncopeTO.setGroupWorkflowAdapter(AopUtils.getTargetClass(gwfAdapter).getName());
@@ -138,6 +132,7 @@ public class SyncopeLogic extends AbstractLogic<SyncopeTO> {
         syncopeTO.getAccountRules().addAll(implementationLookup.getClassNames(Type.ACCOUNT_RULE));
         syncopeTO.getPasswordRules().addAll(implementationLookup.getClassNames(Type.PASSWORD_RULE));
         syncopeTO.getTaskJobs().addAll(implementationLookup.getClassNames(Type.TASKJOBDELEGATE));
+        syncopeTO.getLogicActions().addAll(implementationLookup.getClassNames(Type.LOGIC_ACTIONS));
         syncopeTO.getPropagationActions().addAll(implementationLookup.getClassNames(Type.PROPAGATION_ACTIONS));
         syncopeTO.getSyncActions().addAll(implementationLookup.getClassNames(Type.SYNC_ACTIONS));
         syncopeTO.getPushActions().addAll(implementationLookup.getClassNames(Type.PUSH_ACTIONS));

http://git-wip-us.apache.org/repos/asf/syncope/blob/00988759/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
index aab476a..f508b47 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
@@ -54,7 +54,7 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecu
 import org.apache.syncope.core.misc.security.AuthContextUtils;
 import org.apache.syncope.core.misc.serialization.POJOHelper;
 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
-import org.apache.syncope.core.provisioning.api.AnyTransformer;
+import org.apache.syncope.core.provisioning.api.LogicActions;
 import org.apache.syncope.core.provisioning.api.VirAttrHandler;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -90,9 +90,6 @@ public class UserLogic extends AbstractAnyLogic<UserTO, UserMod> {
     protected PropagationTaskExecutor taskExecutor;
 
     @Autowired
-    protected AnyTransformer anyTransformer;
-
-    @Autowired
     protected UserProvisioningManager provisioningManager;
 
     @Autowired
@@ -181,7 +178,7 @@ public class UserLogic extends AbstractAnyLogic<UserTO, UserMod> {
 
     @PreAuthorize("isAnonymous() or hasRole('" + Entitlement.ANONYMOUS + "')")
     public UserTO selfCreate(final UserTO userTO, final boolean storePassword) {
-        return doCreate(userTO, storePassword);
+        return doCreate(userTO, storePassword, true);
     }
 
     @PreAuthorize("hasRole('" + Entitlement.USER_CREATE + "')")
@@ -192,68 +189,69 @@ public class UserLogic extends AbstractAnyLogic<UserTO, UserMod> {
 
     @PreAuthorize("hasRole('" + Entitlement.USER_CREATE + "')")
     public UserTO create(final UserTO userTO, final boolean storePassword) {
-        if (userTO.getRealm() == null) {
+        return doCreate(userTO, storePassword, false);
+    }
+
+    protected UserTO doCreate(final UserTO userTO, final boolean storePassword, final boolean self) {
+        Pair<UserTO, List<LogicActions>> before = beforeCreate(userTO);
+
+        if (before.getLeft().getRealm() == null) {
             throw SyncopeClientException.build(ClientExceptionType.InvalidRealm);
         }
-        // security checks
-        Set<String> effectiveRealms = getEffectiveRealms(
-                AuthContextUtils.getAuthorizations().get(Entitlement.USER_CREATE),
-                Collections.singleton(userTO.getRealm()));
-        securityChecks(effectiveRealms, userTO.getRealm(), null);
-
-        return doCreate(userTO, storePassword);
-    }
 
-    protected UserTO doCreate(final UserTO userTO, final boolean storePassword) {
-        // Any transformation (if configured)
-        UserTO actual = anyTransformer.transform(userTO);
-        LOG.debug("Transformed: {}", actual);
+        if (!self) {
+            Set<String> effectiveRealms = getEffectiveRealms(
+                    AuthContextUtils.getAuthorizations().get(Entitlement.USER_CREATE),
+                    Collections.singleton(before.getLeft().getRealm()));
+            securityChecks(effectiveRealms, before.getLeft().getRealm(), null);
+        }
 
-        Map.Entry<Long, List<PropagationStatus>> created = provisioningManager.create(actual, storePassword);
+        Map.Entry<Long, List<PropagationStatus>> created = provisioningManager.create(before.getLeft(), storePassword);
 
         UserTO savedTO = binder.getUserTO(created.getKey());
         savedTO.getPropagationStatusTOs().addAll(created.getValue());
-        return savedTO;
+
+        return afterCreate(savedTO, before.getValue());
     }
 
     @PreAuthorize("isAuthenticated() and not(hasRole('" + Entitlement.ANONYMOUS + "'))")
     public UserTO selfUpdate(final UserMod userMod) {
         UserTO userTO = binder.getAuthenticatedUserTO();
         userMod.setKey(userTO.getKey());
-        return doUpdate(userMod);
+        return doUpdate(userMod, true);
     }
 
     @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
     @Override
     public UserTO update(final UserMod userMod) {
-        // Any transformation (if configured)
-        UserMod actual = anyTransformer.transform(userMod);
-        LOG.debug("Transformed: {}", actual);
+        return doUpdate(userMod, false);
+    }
 
-        // security checks
-        UserTO toUpdate = binder.getUserTO(userMod.getKey());
-        Set<String> requestedRealms = new HashSet<>();
-        requestedRealms.add(toUpdate.getRealm());
-        if (StringUtils.isNotBlank(actual.getRealm())) {
-            requestedRealms.add(actual.getRealm());
-        }
-        Set<String> effectiveRealms = getEffectiveRealms(
-                AuthContextUtils.getAuthorizations().get(Entitlement.USER_UPDATE),
-                requestedRealms);
-        securityChecks(effectiveRealms, toUpdate.getRealm(), toUpdate.getKey());
-        if (StringUtils.isNotBlank(actual.getRealm())) {
-            securityChecks(effectiveRealms, actual.getRealm(), toUpdate.getKey());
-        }
+    protected UserTO doUpdate(final UserMod userMod, final boolean self) {
+        UserTO userTO = binder.getUserTO(userMod.getKey());
+        Pair<UserMod, List<LogicActions>> before = beforeUpdate(userMod, userTO.getRealm());
 
-        return doUpdate(actual);
-    }
+        if (!self) {
+            Set<String> requestedRealms = new HashSet<>();
+            requestedRealms.add(before.getLeft().getRealm());
+            if (StringUtils.isNotBlank(before.getLeft().getRealm())) {
+                requestedRealms.add(before.getLeft().getRealm());
+            }
+            Set<String> effectiveRealms = getEffectiveRealms(
+                    AuthContextUtils.getAuthorizations().get(Entitlement.USER_UPDATE),
+                    requestedRealms);
+            securityChecks(effectiveRealms, before.getLeft().getRealm(), before.getLeft().getKey());
+            if (StringUtils.isNotBlank(before.getLeft().getRealm())) {
+                securityChecks(effectiveRealms, before.getLeft().getRealm(), before.getLeft().getKey());
+            }
+        }
 
-    protected UserTO doUpdate(final UserMod userMod) {
-        Map.Entry<Long, List<PropagationStatus>> updated = provisioningManager.update(userMod);
+        Map.Entry<Long, List<PropagationStatus>> updated = provisioningManager.update(before.getLeft());
 
         UserTO updatedTO = binder.getUserTO(updated.getKey());
         updatedTO.getPropagationStatusTOs().addAll(updated.getValue());
-        return updatedTO;
+
+        return afterUpdate(updatedTO, before.getRight());
     }
 
     protected Map.Entry<Long, List<PropagationStatus>> setStatusOnWfAdapter(final StatusMod statusMod) {
@@ -334,25 +332,27 @@ public class UserLogic extends AbstractAnyLogic<UserTO, UserMod> {
     @PreAuthorize("isAuthenticated() and not(hasRole('" + Entitlement.ANONYMOUS + "'))")
     public UserTO selfDelete() {
         UserTO userTO = binder.getAuthenticatedUserTO();
-
-        return doDelete(userTO.getKey());
+        return doDelete(userTO, true);
     }
 
     @PreAuthorize("hasRole('" + Entitlement.USER_DELETE + "')")
     @Override
     public UserTO delete(final Long key) {
-        // security checks
-        UserTO toDelete = binder.getUserTO(key);
-        Set<String> effectiveRealms = getEffectiveRealms(
-                AuthContextUtils.getAuthorizations().get(Entitlement.USER_DELETE),
-                Collections.singleton(toDelete.getRealm()));
-        securityChecks(effectiveRealms, toDelete.getRealm(), toDelete.getKey());
-
-        return doDelete(key);
+        UserTO userTO = binder.getUserTO(key);
+        return doDelete(userTO, false);
     }
 
-    protected UserTO doDelete(final Long key) {
-        List<Group> ownedGroups = groupDAO.findOwnedByUser(key);
+    protected UserTO doDelete(final UserTO userTO, final boolean self) {
+        Pair<UserTO, List<LogicActions>> before = beforeDelete(userTO);
+
+        if (!self) {
+            Set<String> effectiveRealms = getEffectiveRealms(
+                    AuthContextUtils.getAuthorizations().get(Entitlement.USER_DELETE),
+                    Collections.singleton(before.getLeft().getRealm()));
+            securityChecks(effectiveRealms, before.getLeft().getRealm(), before.getLeft().getKey());
+        }
+
+        List<Group> ownedGroups = groupDAO.findOwnedByUser(before.getLeft().getKey());
         if (!ownedGroups.isEmpty()) {
             SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.GroupOwnership);
             sce.getElements().addAll(CollectionUtils.collect(ownedGroups, new Transformer<Group, String>() {
@@ -365,18 +365,18 @@ public class UserLogic extends AbstractAnyLogic<UserTO, UserMod> {
             throw sce;
         }
 
-        List<PropagationStatus> statuses = provisioningManager.delete(key);
+        List<PropagationStatus> statuses = provisioningManager.delete(before.getLeft().getKey());
 
         UserTO deletedTO;
-        if (userDAO.find(key) == null) {
+        if (userDAO.find(before.getLeft().getKey()) == null) {
             deletedTO = new UserTO();
-            deletedTO.setKey(key);
+            deletedTO.setKey(before.getLeft().getKey());
         } else {
-            deletedTO = binder.getUserTO(key);
+            deletedTO = binder.getUserTO(before.getLeft().getKey());
         }
         deletedTO.getPropagationStatusTOs().addAll(statuses);
 
-        return deletedTO;
+        return afterDelete(deletedTO, before.getRight());
     }
 
     @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")

http://git-wip-us.apache.org/repos/asf/syncope/blob/00988759/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java b/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
index 2305e70..d68df96 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
@@ -37,6 +37,7 @@ import org.apache.syncope.core.persistence.api.dao.AccountRule;
 import org.apache.syncope.core.persistence.api.dao.AccountRuleConfClass;
 import org.apache.syncope.core.persistence.api.dao.PasswordRule;
 import org.apache.syncope.core.persistence.api.dao.PasswordRuleConfClass;
+import org.apache.syncope.core.provisioning.api.LogicActions;
 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.sync.PushActions;
@@ -90,12 +91,13 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
         scanner.addIncludeFilter(new AssignableTypeFilter(AccountRule.class));
         scanner.addIncludeFilter(new AssignableTypeFilter(PasswordRule.class));
         scanner.addIncludeFilter(new AssignableTypeFilter(SchedTaskJobDelegate.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(LogicActions.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(PropagationActions.class));
         scanner.addIncludeFilter(new AssignableTypeFilter(SyncActions.class));
         scanner.addIncludeFilter(new AssignableTypeFilter(PushActions.class));
         scanner.addIncludeFilter(new AssignableTypeFilter(SyncCorrelationRule.class));
         // Remove once SYNCOPE-470 is done
         //scanner.addIncludeFilter(new AssignableTypeFilter(PushCorrelationRule.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(PropagationActions.class));
         scanner.addIncludeFilter(new AssignableTypeFilter(Validator.class));
 
         for (BeanDefinition bd : scanner.findCandidateComponents(StringUtils.EMPTY)) {
@@ -136,6 +138,14 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
                     classNames.get(Type.TASKJOBDELEGATE).add(bd.getBeanClassName());
                 }
 
+                if (LogicActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                    classNames.get(Type.LOGIC_ACTIONS).add(bd.getBeanClassName());
+                }
+
+                if (PropagationActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                    classNames.get(Type.PROPAGATION_ACTIONS).add(bd.getBeanClassName());
+                }
+
                 if (SyncActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
                     classNames.get(Type.SYNC_ACTIONS).add(bd.getBeanClassName());
                 }
@@ -152,10 +162,6 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
                 /* if (PushCorrelationRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
                  * classNames.get(Type.PUSH_CORRELATION_RULES).add(metadata.getClassName());
                  * } */
-                if (PropagationActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
-                    classNames.get(Type.PROPAGATION_ACTIONS).add(bd.getBeanClassName());
-                }
-
                 if (Validator.class.isAssignableFrom(clazz) && !isAbsractClazz) {
                     classNames.get(Type.VALIDATOR).add(bd.getBeanClassName());
                 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/00988759/core/logic/src/main/resources/logic.properties
----------------------------------------------------------------------
diff --git a/core/logic/src/main/resources/logic.properties b/core/logic/src/main/resources/logic.properties
index d2f6b11..4af398a 100644
--- a/core/logic/src/main/resources/logic.properties
+++ b/core/logic/src/main/resources/logic.properties
@@ -14,5 +14,4 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-anyTransformer=org.apache.syncope.core.provisioning.java.DefaultAnyTransformer
 logicInvocationHandler=org.apache.syncope.core.logic.LogicInvocationHandler

http://git-wip-us.apache.org/repos/asf/syncope/blob/00988759/core/logic/src/main/resources/logicContext.xml
----------------------------------------------------------------------
diff --git a/core/logic/src/main/resources/logicContext.xml b/core/logic/src/main/resources/logicContext.xml
index b70f4cd..5bec085 100644
--- a/core/logic/src/main/resources/logicContext.xml
+++ b/core/logic/src/main/resources/logicContext.xml
@@ -37,6 +37,5 @@ under the License.
   <context:component-scan base-package="org.apache.syncope.core.logic"/>
 
   <bean class="${logicInvocationHandler}"/>
-  <bean class="${anyTransformer}"/>
 
 </beans>

http://git-wip-us.apache.org/repos/asf/syncope/blob/00988759/core/misc/src/main/java/org/apache/syncope/core/misc/ConnObjectUtils.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/ConnObjectUtils.java b/core/misc/src/main/java/org/apache/syncope/core/misc/ConnObjectUtils.java
index 5ab5f44..02c6e62 100644
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/ConnObjectUtils.java
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/ConnObjectUtils.java
@@ -35,8 +35,6 @@ import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.to.AttrTO;
 import org.apache.syncope.common.lib.to.ConnObjectTO;
 import org.apache.syncope.common.lib.to.GroupTO;
-import org.apache.syncope.common.lib.to.MembershipTO;
-import org.apache.syncope.common.lib.to.RelationshipTO;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.AttrSchemaType;
@@ -44,23 +42,19 @@ import org.apache.syncope.common.lib.types.MappingPurpose;
 import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
-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.AnyUtils;
 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.PlainAttrValue;
 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
-import org.apache.syncope.core.persistence.api.entity.group.Group;
 import org.apache.syncope.core.persistence.api.entity.task.SyncTask;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.misc.security.Encryptor;
-import org.apache.syncope.core.misc.jexl.JexlUtils;
 import org.apache.syncope.core.misc.security.PasswordGenerator;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.resource.Provision;
-import org.apache.syncope.core.persistence.api.entity.task.AnyTemplate;
 import org.identityconnectors.common.Base64;
 import org.identityconnectors.common.security.GuardedByteArray;
 import org.identityconnectors.common.security.GuardedString;
@@ -78,13 +72,13 @@ public class ConnObjectUtils {
     private static final Logger LOG = LoggerFactory.getLogger(ConnObjectUtils.class);
 
     @Autowired
-    private RealmDAO realmDAO;
+    private TemplateUtils templateUtils;
 
     @Autowired
-    private UserDAO userDAO;
+    private RealmDAO realmDAO;
 
     @Autowired
-    private GroupDAO groupDAO;
+    private UserDAO userDAO;
 
     @Autowired
     private ExternalResourceDAO resourceDAO;
@@ -320,57 +314,7 @@ public class ConnObjectUtils {
         }
 
         // 2. add data from defined template (if any)
-        AnyTemplate anyTypeTemplate = syncTask.getTemplate(provision.getAnyType());
-        if (anyTypeTemplate != null) {
-            AnyTO template = anyTypeTemplate.get();
-            fillFromTemplate(anyTO, template);
-
-            if (template instanceof AnyObjectTO) {
-                fillRelationshipsFromTemplate(((AnyObjectTO) anyTO).getRelationshipMap(),
-                        ((AnyObjectTO) anyTO).getRelationships(), ((AnyObjectTO) template).getRelationships());
-                fillMembershipsFromTemplate(((AnyObjectTO) anyTO).getMembershipMap(),
-                        ((AnyObjectTO) anyTO).getMemberships(), ((AnyObjectTO) template).getMemberships());
-            } else if (template instanceof UserTO) {
-                if (StringUtils.isNotBlank(((UserTO) template).getUsername())) {
-                    String evaluated = JexlUtils.evaluate(((UserTO) template).getUsername(), anyTO);
-                    if (StringUtils.isNotBlank(evaluated)) {
-                        ((UserTO) anyTO).setUsername(evaluated);
-                    }
-                }
-
-                if (StringUtils.isNotBlank(((UserTO) template).getPassword())) {
-                    String evaluated = JexlUtils.evaluate(((UserTO) template).getPassword(), anyTO);
-                    if (StringUtils.isNotBlank(evaluated)) {
-                        ((UserTO) anyTO).setPassword(evaluated);
-                    }
-                }
-
-                fillRelationshipsFromTemplate(((UserTO) anyTO).getRelationshipMap(),
-                        ((UserTO) anyTO).getRelationships(), ((UserTO) template).getRelationships());
-                fillMembershipsFromTemplate(((UserTO) anyTO).getMembershipMap(),
-                        ((UserTO) anyTO).getMemberships(), ((UserTO) template).getMemberships());
-            } else if (template instanceof GroupTO) {
-                if (StringUtils.isNotBlank(((GroupTO) template).getName())) {
-                    String evaluated = JexlUtils.evaluate(((GroupTO) template).getName(), anyTO);
-                    if (StringUtils.isNotBlank(evaluated)) {
-                        ((GroupTO) anyTO).setName(evaluated);
-                    }
-                }
-
-                if (((GroupTO) template).getUserOwner() != null) {
-                    final User userOwner = userDAO.find(((GroupTO) template).getUserOwner());
-                    if (userOwner != null) {
-                        ((GroupTO) anyTO).setUserOwner(userOwner.getKey());
-                    }
-                }
-                if (((GroupTO) template).getGroupOwner() != null) {
-                    final Group groupOwner = groupDAO.find(((GroupTO) template).getGroupOwner());
-                    if (groupOwner != null) {
-                        ((GroupTO) anyTO).setGroupOwner(groupOwner.getKey());
-                    }
-                }
-            }
-        }
+        templateUtils.apply(anyTO, syncTask.getTemplate(provision.getAnyType()));
 
         return anyTO;
     }
@@ -442,81 +386,6 @@ public class ConnObjectUtils {
         return connObjectTO;
     }
 
-    private AttrTO evaluateAttrFromTemplate(final AnyTO anyTO, final AttrTO template) {
-        AttrTO result = new AttrTO();
-        result.setSchema(template.getSchema());
-
-        if (template.getValues() != null && !template.getValues().isEmpty()) {
-            for (String value : template.getValues()) {
-                String evaluated = JexlUtils.evaluate(value, anyTO);
-                if (StringUtils.isNotBlank(evaluated)) {
-                    result.getValues().add(evaluated);
-                }
-            }
-        }
-
-        return result;
-    }
-
-    private void fillFromTemplate(final AnyTO anyTO, final AnyTO template) {
-        if (template.getRealm() != null) {
-            anyTO.setRealm(template.getRealm());
-        }
-
-        Map<String, AttrTO> currentAttrMap = anyTO.getPlainAttrMap();
-        for (AttrTO templatePlainAttr : template.getPlainAttrs()) {
-            if (!templatePlainAttr.getValues().isEmpty()
-                    && (!currentAttrMap.containsKey(templatePlainAttr.getSchema())
-                    || currentAttrMap.get(templatePlainAttr.getSchema()).getValues().isEmpty())) {
-
-                anyTO.getPlainAttrs().add(evaluateAttrFromTemplate(anyTO, templatePlainAttr));
-            }
-        }
-
-        currentAttrMap = anyTO.getDerAttrMap();
-        for (AttrTO templateDerAttr : template.getDerAttrs()) {
-            if (!currentAttrMap.containsKey(templateDerAttr.getSchema())) {
-                anyTO.getDerAttrs().add(templateDerAttr);
-            }
-        }
-
-        currentAttrMap = anyTO.getVirAttrMap();
-        for (AttrTO templateVirAttr : template.getVirAttrs()) {
-            if (!templateVirAttr.getValues().isEmpty()
-                    && (!currentAttrMap.containsKey(templateVirAttr.getSchema())
-                    || currentAttrMap.get(templateVirAttr.getSchema()).getValues().isEmpty())) {
-
-                anyTO.getVirAttrs().add(evaluateAttrFromTemplate(anyTO, templateVirAttr));
-            }
-        }
-
-        for (String resource : template.getResources()) {
-            anyTO.getResources().add(resource);
-        }
-
-        anyTO.getAuxClasses().addAll(template.getAuxClasses());
-    }
-
-    private void fillRelationshipsFromTemplate(final Map<Long, RelationshipTO> anyRelMap,
-            final List<RelationshipTO> anyRels, final List<RelationshipTO> templateRels) {
-
-        for (RelationshipTO memb : templateRels) {
-            if (!anyRelMap.containsKey(memb.getRightKey())) {
-                anyRels.add(memb);
-            }
-        }
-    }
-
-    private void fillMembershipsFromTemplate(final Map<Long, MembershipTO> anyMembMap,
-            final List<MembershipTO> anyMembs, final List<MembershipTO> templateMembs) {
-
-        for (MembershipTO memb : templateMembs) {
-            if (!anyMembMap.containsKey(memb.getRightKey())) {
-                anyMembs.add(memb);
-            }
-        }
-    }
-
     /**
      * Transform a
      * <code>Collection</code> of {@link Attribute} instances into a {@link Map}. The key to each element in the map is

http://git-wip-us.apache.org/repos/asf/syncope/blob/00988759/core/misc/src/main/java/org/apache/syncope/core/misc/TemplateUtils.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/TemplateUtils.java b/core/misc/src/main/java/org/apache/syncope/core/misc/TemplateUtils.java
new file mode 100644
index 0000000..d990cdb
--- /dev/null
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/TemplateUtils.java
@@ -0,0 +1,223 @@
+/*
+ * 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.misc;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.AnyObjectTO;
+import org.apache.syncope.common.lib.to.AnyTO;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.common.lib.to.GroupTO;
+import org.apache.syncope.common.lib.to.MembershipTO;
+import org.apache.syncope.common.lib.to.RelationshipTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.core.misc.jexl.JexlUtils;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.AnyTemplate;
+import org.apache.syncope.core.persistence.api.entity.group.Group;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+public class TemplateUtils {
+
+    @Autowired
+    private UserDAO userDAO;
+
+    @Autowired
+    private GroupDAO groupDAO;
+
+    private AttrTO evaluateAttr(final AnyTO anyTO, final AttrTO template) {
+        AttrTO result = new AttrTO();
+        result.setSchema(template.getSchema());
+
+        if (template.getValues() != null && !template.getValues().isEmpty()) {
+            for (String value : template.getValues()) {
+                String evaluated = JexlUtils.evaluate(value, anyTO);
+                if (StringUtils.isNotBlank(evaluated)) {
+                    result.getValues().add(evaluated);
+                }
+            }
+        }
+
+        return result;
+    }
+
+    private void fill(final AnyTO anyTO, final AnyTO template) {
+        if (template.getRealm() != null) {
+            anyTO.setRealm(template.getRealm());
+        }
+
+        Map<String, AttrTO> currentAttrMap = anyTO.getPlainAttrMap();
+        for (AttrTO templatePlainAttr : template.getPlainAttrs()) {
+            if (!templatePlainAttr.getValues().isEmpty()
+                    && (!currentAttrMap.containsKey(templatePlainAttr.getSchema())
+                    || currentAttrMap.get(templatePlainAttr.getSchema()).getValues().isEmpty())) {
+
+                anyTO.getPlainAttrs().add(evaluateAttr(anyTO, templatePlainAttr));
+            }
+        }
+
+        currentAttrMap = anyTO.getDerAttrMap();
+        for (AttrTO templateDerAttr : template.getDerAttrs()) {
+            if (!currentAttrMap.containsKey(templateDerAttr.getSchema())) {
+                anyTO.getDerAttrs().add(templateDerAttr);
+            }
+        }
+
+        currentAttrMap = anyTO.getVirAttrMap();
+        for (AttrTO templateVirAttr : template.getVirAttrs()) {
+            if (!templateVirAttr.getValues().isEmpty()
+                    && (!currentAttrMap.containsKey(templateVirAttr.getSchema())
+                    || currentAttrMap.get(templateVirAttr.getSchema()).getValues().isEmpty())) {
+
+                anyTO.getVirAttrs().add(evaluateAttr(anyTO, templateVirAttr));
+            }
+        }
+
+        for (String resource : template.getResources()) {
+            anyTO.getResources().add(resource);
+        }
+
+        anyTO.getAuxClasses().addAll(template.getAuxClasses());
+    }
+
+    private void fillRelationships(final Map<Long, RelationshipTO> anyRelMap,
+            final List<RelationshipTO> anyRels, final List<RelationshipTO> templateRels) {
+
+        for (RelationshipTO memb : templateRels) {
+            if (!anyRelMap.containsKey(memb.getRightKey())) {
+                anyRels.add(memb);
+            }
+        }
+    }
+
+    private void fillMemberships(final Map<Long, MembershipTO> anyMembMap,
+            final List<MembershipTO> anyMembs, final List<MembershipTO> templateMembs) {
+
+        for (MembershipTO memb : templateMembs) {
+            if (!anyMembMap.containsKey(memb.getRightKey())) {
+                anyMembs.add(memb);
+            }
+        }
+    }
+
+    @Transactional(readOnly = true)
+    public <T extends AnyTO> void apply(final T anyTO, final AnyTemplate anyTemplate) {
+        if (anyTemplate != null) {
+            AnyTO template = anyTemplate.get();
+            fill(anyTO, template);
+
+            if (template instanceof AnyObjectTO) {
+                fillRelationships(((AnyObjectTO) anyTO).getRelationshipMap(),
+                        ((AnyObjectTO) anyTO).getRelationships(), ((AnyObjectTO) template).getRelationships());
+                fillMemberships(((AnyObjectTO) anyTO).getMembershipMap(),
+                        ((AnyObjectTO) anyTO).getMemberships(), ((AnyObjectTO) template).getMemberships());
+            } else if (template instanceof UserTO) {
+                if (StringUtils.isNotBlank(((UserTO) template).getUsername())) {
+                    String evaluated = JexlUtils.evaluate(((UserTO) template).getUsername(), anyTO);
+                    if (StringUtils.isNotBlank(evaluated)) {
+                        ((UserTO) anyTO).setUsername(evaluated);
+                    }
+                }
+
+                if (StringUtils.isNotBlank(((UserTO) template).getPassword())) {
+                    String evaluated = JexlUtils.evaluate(((UserTO) template).getPassword(), anyTO);
+                    if (StringUtils.isNotBlank(evaluated)) {
+                        ((UserTO) anyTO).setPassword(evaluated);
+                    }
+                }
+
+                fillRelationships(((UserTO) anyTO).getRelationshipMap(),
+                        ((UserTO) anyTO).getRelationships(), ((UserTO) template).getRelationships());
+                fillMemberships(((UserTO) anyTO).getMembershipMap(),
+                        ((UserTO) anyTO).getMemberships(), ((UserTO) template).getMemberships());
+            } else if (template instanceof GroupTO) {
+                if (StringUtils.isNotBlank(((GroupTO) template).getName())) {
+                    String evaluated = JexlUtils.evaluate(((GroupTO) template).getName(), anyTO);
+                    if (StringUtils.isNotBlank(evaluated)) {
+                        ((GroupTO) anyTO).setName(evaluated);
+                    }
+                }
+
+                if (((GroupTO) template).getUserOwner() != null) {
+                    final User userOwner = userDAO.find(((GroupTO) template).getUserOwner());
+                    if (userOwner != null) {
+                        ((GroupTO) anyTO).setUserOwner(userOwner.getKey());
+                    }
+                }
+                if (((GroupTO) template).getGroupOwner() != null) {
+                    final Group groupOwner = groupDAO.find(((GroupTO) template).getGroupOwner());
+                    if (groupOwner != null) {
+                        ((GroupTO) anyTO).setGroupOwner(groupOwner.getKey());
+                    }
+                }
+            }
+        }
+    }
+
+    public void check(final Map<String, AnyTO> templates, final ClientExceptionType clientExceptionType) {
+        SyncopeClientException sce = SyncopeClientException.build(clientExceptionType);
+
+        for (Map.Entry<String, AnyTO> entry : templates.entrySet()) {
+            for (AttrTO attrTO : entry.getValue().getPlainAttrs()) {
+                if (!attrTO.getValues().isEmpty() && !JexlUtils.isExpressionValid(attrTO.getValues().get(0))) {
+                    sce.getElements().add("Invalid JEXL: " + attrTO.getValues().get(0));
+                }
+            }
+
+            for (AttrTO attrTO : entry.getValue().getVirAttrs()) {
+                if (!attrTO.getValues().isEmpty() && !JexlUtils.isExpressionValid(attrTO.getValues().get(0))) {
+                    sce.getElements().add("Invalid JEXL: " + attrTO.getValues().get(0));
+                }
+            }
+
+            if (entry.getValue() instanceof UserTO) {
+                UserTO template = (UserTO) entry.getValue();
+                if (StringUtils.isNotBlank(template.getUsername())
+                        && !JexlUtils.isExpressionValid(template.getUsername())) {
+
+                    sce.getElements().add("Invalid JEXL: " + template.getUsername());
+                }
+                if (StringUtils.isNotBlank(template.getPassword())
+                        && !JexlUtils.isExpressionValid(template.getPassword())) {
+
+                    sce.getElements().add("Invalid JEXL: " + template.getPassword());
+                }
+            } else if (entry.getValue() instanceof GroupTO) {
+                GroupTO template = (GroupTO) entry.getValue();
+                if (StringUtils.isNotBlank(template.getName())
+                        && !JexlUtils.isExpressionValid(template.getName())) {
+
+                    sce.getElements().add("Invalid JEXL: " + template.getName());
+                }
+            }
+        }
+
+        if (!sce.isEmpty()) {
+            throw sce;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/00988759/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java
index b00d3d4..530b406 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java
@@ -34,6 +34,7 @@ public interface ImplementationLookup extends SyncopeLoader {
         ACCOUNT_RULE,
         PASSWORD_RULE,
         TASKJOBDELEGATE,
+        LOGIC_ACTIONS,
         PROPAGATION_ACTIONS,
         SYNC_ACTIONS,
         PUSH_ACTIONS,

http://git-wip-us.apache.org/repos/asf/syncope/blob/00988759/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AnyTemplate.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AnyTemplate.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AnyTemplate.java
new file mode 100644
index 0000000..c00d3a4
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AnyTemplate.java
@@ -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.
+ */
+package org.apache.syncope.core.persistence.api.entity;
+
+import org.apache.syncope.common.lib.to.AnyTO;
+
+public interface AnyTemplate extends Entity<Long> {
+
+    AnyType getAnyType();
+
+    void setAnyType(AnyType anyType);
+
+    AnyTO get();
+
+    void set(AnyTO template);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/00988759/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AnyTemplateRealm.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AnyTemplateRealm.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AnyTemplateRealm.java
new file mode 100644
index 0000000..9caa4ad
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AnyTemplateRealm.java
@@ -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;
+
+public interface AnyTemplateRealm extends AnyTemplate {
+
+    Realm getRealm();
+
+    void setRealm(Realm realm);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/00988759/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Realm.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Realm.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Realm.java
index ae3290f..f973138 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Realm.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Realm.java
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.core.persistence.api.entity;
 
+import java.util.List;
+import java.util.Set;
 import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 
@@ -28,7 +30,7 @@ public interface Realm extends Entity<Long> {
     Realm getParent();
 
     String getFullPath();
-    
+
     AccountPolicy getAccountPolicy();
 
     PasswordPolicy getPasswordPolicy();
@@ -41,4 +43,13 @@ public interface Realm extends Entity<Long> {
 
     void setPasswordPolicy(PasswordPolicy passwordPolicy);
 
+    Set<String> getActionsClassNames();
+
+    boolean add(AnyTemplateRealm template);
+
+    boolean remove(AnyTemplateRealm template);
+
+    AnyTemplateRealm getTemplate(AnyType anyType);
+
+    List<? extends AnyTemplateRealm> getTemplates();
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/00988759/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/AnyTemplate.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/AnyTemplate.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/AnyTemplate.java
deleted file mode 100644
index fa43dc3..0000000
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/AnyTemplate.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.persistence.api.entity.task;
-
-import org.apache.syncope.common.lib.to.AnyTO;
-import org.apache.syncope.core.persistence.api.entity.AnyType;
-import org.apache.syncope.core.persistence.api.entity.Entity;
-
-public interface AnyTemplate extends Entity<Long> {
-
-    SyncTask getSyncTask();
-
-    void setSyncTask(SyncTask syncTask);
-
-    AnyType getAnyType();
-
-    void setAnyType(AnyType anyType);
-
-    AnyTO get();
-
-    void set(AnyTO template);
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/00988759/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/AnyTemplateSyncTask.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/AnyTemplateSyncTask.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/AnyTemplateSyncTask.java
new file mode 100644
index 0000000..82d7e2e
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/AnyTemplateSyncTask.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.entity.task;
+
+import org.apache.syncope.core.persistence.api.entity.AnyTemplate;
+
+public interface AnyTemplateSyncTask extends AnyTemplate {
+
+    SyncTask getSyncTask();
+
+    void setSyncTask(SyncTask syncTask);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/00988759/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/SyncTask.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/SyncTask.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/SyncTask.java
index 7550ed8..bc0c2b2 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/SyncTask.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/task/SyncTask.java
@@ -32,11 +32,11 @@ public interface SyncTask extends ProvisioningTask {
 
     void setFullReconciliation(boolean condition);
 
-    boolean add(AnyTemplate template);
+    boolean add(AnyTemplateSyncTask template);
 
-    boolean remove(AnyTemplate template);
+    boolean remove(AnyTemplateSyncTask template);
 
-    AnyTemplate getTemplate(AnyType anyType);
+    AnyTemplateSyncTask getTemplate(AnyType anyType);
 
-    List<? extends AnyTemplate> getTemplates();
+    List<? extends AnyTemplateSyncTask> getTemplates();
 }


Mime
View raw message