You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2015/08/02 07:45:26 UTC

[12/15] syncope git commit: [SYNCOPE-652] Still several things to refine, but it starts taking shape

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAReportDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAReportDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAReportDAO.java
index 793411d..6de5ffb 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAReportDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAReportDAO.java
@@ -32,12 +32,13 @@ import org.springframework.transaction.annotation.Transactional;
 @Repository
 public class JPAReportDAO extends AbstractDAO<Report, Long> implements ReportDAO {
 
-    @Override
     @Transactional(readOnly = true)
+    @Override
     public Report find(final Long key) {
-        return entityManager.find(JPAReport.class, key);
+        return entityManager().find(JPAReport.class, key);
     }
 
+    @Transactional(readOnly = true)
     @Override
     public List<Report> findAll() {
         return findAll(-1, -1, Collections.<OrderByClause>emptyList());
@@ -45,7 +46,7 @@ public class JPAReportDAO extends AbstractDAO<Report, Long> implements ReportDAO
 
     @Override
     public List<Report> findAll(final int page, final int itemsPerPage, final List<OrderByClause> orderByClauses) {
-        final TypedQuery<Report> query = entityManager.createQuery(
+        final TypedQuery<Report> query = entityManager().createQuery(
                 "SELECT e FROM " + JPAReport.class.getSimpleName() + " e "
                 + toOrderByStatement(Report.class, "e", orderByClauses), Report.class);
 
@@ -62,14 +63,14 @@ public class JPAReportDAO extends AbstractDAO<Report, Long> implements ReportDAO
 
     @Override
     public int count() {
-        Query countQuery = entityManager.createNativeQuery("SELECT COUNT(id) FROM " + JPAReport.TABLE);
+        Query countQuery = entityManager().createNativeQuery("SELECT COUNT(id) FROM " + JPAReport.TABLE);
         return ((Number) countQuery.getSingleResult()).intValue();
     }
 
     @Override
     @Transactional(rollbackFor = Throwable.class)
     public Report save(final Report report) {
-        return entityManager.merge(report);
+        return entityManager().merge(report);
     }
 
     @Override
@@ -84,6 +85,6 @@ public class JPAReportDAO extends AbstractDAO<Report, Long> implements ReportDAO
 
     @Override
     public void delete(final Report report) {
-        entityManager.remove(report);
+        entityManager().remove(report);
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAReportExecDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAReportExecDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAReportExecDAO.java
index 95f57df..cf9e781 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAReportExecDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAReportExecDAO.java
@@ -32,11 +32,11 @@ public class JPAReportExecDAO extends AbstractDAO<ReportExec, Long> implements R
 
     @Override
     public ReportExec find(final Long key) {
-        return entityManager.find(JPAReportExec.class, key);
+        return entityManager().find(JPAReportExec.class, key);
     }
 
     private ReportExec findLatest(final Report report, final String field) {
-        TypedQuery<ReportExec> query = entityManager.createQuery(
+        TypedQuery<ReportExec> query = entityManager().createQuery(
                 "SELECT e FROM " + JPAReportExec.class.getSimpleName() + " e "
                 + "WHERE e.report=:report ORDER BY e." + field + " DESC", ReportExec.class);
         query.setParameter("report", report);
@@ -60,7 +60,7 @@ public class JPAReportExecDAO extends AbstractDAO<ReportExec, Long> implements R
 
     @Override
     public List<ReportExec> findAll() {
-        TypedQuery<ReportExec> query = entityManager.createQuery(
+        TypedQuery<ReportExec> query = entityManager().createQuery(
                 "SELECT e FROM " + JPAReportExec.class.getSimpleName() + " e", ReportExec.class);
         return query.getResultList();
     }
@@ -75,7 +75,7 @@ public class JPAReportExecDAO extends AbstractDAO<ReportExec, Long> implements R
     @Override
     @Transactional(rollbackFor = Throwable.class)
     public ReportExec save(final ReportExec execution) {
-        return entityManager.merge(execution);
+        return entityManager().merge(execution);
     }
 
     @Override
@@ -94,6 +94,6 @@ public class JPAReportExecDAO extends AbstractDAO<ReportExec, Long> implements R
             execution.getReport().removeExec(execution);
         }
 
-        entityManager.remove(execution);
+        entityManager().remove(execution);
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
index 5f600b6..6fb5ec3 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARoleDAO.java
@@ -43,12 +43,12 @@ public class JPARoleDAO extends AbstractDAO<Role, Long> implements RoleDAO {
 
     @Override
     public Role find(final Long key) {
-        return entityManager.find(JPARole.class, key);
+        return entityManager().find(JPARole.class, key);
     }
 
     @Override
     public Role find(final String name) {
-        TypedQuery<Role> query = entityManager.createQuery(
+        TypedQuery<Role> query = entityManager().createQuery(
                 "SELECT e FROM " + JPARole.class.getSimpleName() + " e WHERE e.name=:name", Role.class);
         query.setParameter("name", name);
 
@@ -64,7 +64,7 @@ public class JPARoleDAO extends AbstractDAO<Role, Long> implements RoleDAO {
 
     @Override
     public List<Role> findByRealm(final Realm realm) {
-        TypedQuery<Role> query = entityManager.createQuery(
+        TypedQuery<Role> query = entityManager().createQuery(
                 "SELECT e FROM " + JPARole.class.getSimpleName() + " e WHERE :realm MEMBER OF e.realms", Role.class);
         query.setParameter("realm", realm);
         return query.getResultList();
@@ -72,7 +72,7 @@ public class JPARoleDAO extends AbstractDAO<Role, Long> implements RoleDAO {
 
     @Override
     public List<Role> findAll() {
-        TypedQuery<Role> query = entityManager.createQuery(
+        TypedQuery<Role> query = entityManager().createQuery(
                 "SELECT e FROM " + JPARole.class.getSimpleName() + " e ", Role.class);
         return query.getResultList();
     }
@@ -90,12 +90,12 @@ public class JPARoleDAO extends AbstractDAO<Role, Long> implements RoleDAO {
             }
         }
 
-        return entityManager.merge(role);
+        return entityManager().merge(role);
     }
 
     @Override
     public void delete(final Role role) {
-        entityManager.remove(role);
+        entityManager().remove(role);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPASecurityQuestionDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPASecurityQuestionDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPASecurityQuestionDAO.java
index 5c242d6..7028080 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPASecurityQuestionDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPASecurityQuestionDAO.java
@@ -36,19 +36,19 @@ public class JPASecurityQuestionDAO extends AbstractDAO<SecurityQuestion, Long>
 
     @Override
     public SecurityQuestion find(final Long key) {
-        return entityManager.find(JPASecurityQuestion.class, key);
+        return entityManager().find(JPASecurityQuestion.class, key);
     }
 
     @Override
     public List<SecurityQuestion> findAll() {
-        final TypedQuery<SecurityQuestion> query = entityManager.createQuery(
+        final TypedQuery<SecurityQuestion> query = entityManager().createQuery(
                 "SELECT e FROM " + JPASecurityQuestion.class.getSimpleName() + " e ", SecurityQuestion.class);
         return query.getResultList();
     }
 
     @Override
     public SecurityQuestion save(final SecurityQuestion securityQuestion) {
-        return entityManager.merge(securityQuestion);
+        return entityManager().merge(securityQuestion);
     }
 
     @Override
@@ -64,7 +64,7 @@ public class JPASecurityQuestionDAO extends AbstractDAO<SecurityQuestion, Long>
             userDAO.save(user);
         }
 
-        entityManager.remove(securityQuestion);
+        entityManager().remove(securityQuestion);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java
index ff93071..a70f65e 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java
@@ -75,7 +75,7 @@ public class JPATaskDAO extends AbstractDAO<Task, Long> implements TaskDAO {
     @SuppressWarnings("unchecked")
     @Override
     public <T extends Task> T find(final Long key) {
-        return (T) entityManager.find(JPATask.class, key);
+        return (T) entityManager().find(JPATask.class, key);
     }
 
     private <T extends Task> StringBuilder buildfindAllQuery(final TaskType type) {
@@ -96,7 +96,7 @@ public class JPATaskDAO extends AbstractDAO<Task, Long> implements TaskDAO {
         }
         queryString.append("ORDER BY e.id DESC");
 
-        Query query = entityManager.createQuery(queryString.toString());
+        Query query = entityManager().createQuery(queryString.toString());
         query.setParameter("type", type);
         return query.getResultList();
     }
@@ -106,13 +106,14 @@ public class JPATaskDAO extends AbstractDAO<Task, Long> implements TaskDAO {
     public <T extends Task> List<T> findAll(final ExternalResource resource, final TaskType type) {
         StringBuilder queryString = buildfindAllQuery(type).append("AND e.resource=:resource ORDER BY e.id DESC");
 
-        final Query query = entityManager.createQuery(queryString.toString());
+        final Query query = entityManager().createQuery(queryString.toString());
         query.setParameter("type", type);
         query.setParameter("resource", resource);
 
         return query.getResultList();
     }
 
+    @Transactional(readOnly = true)
     @Override
     public <T extends Task> List<T> findAll(final TaskType type) {
         return findAll(-1, -1, Collections.<OrderByClause>emptyList(), type);
@@ -128,7 +129,7 @@ public class JPATaskDAO extends AbstractDAO<Task, Long> implements TaskDAO {
                 ? "ORDER BY e.id DESC"
                 : toOrderByStatement(getEntityReference(type), "e", orderByClauses));
 
-        Query query = entityManager.createQuery(queryString.toString());
+        Query query = entityManager().createQuery(queryString.toString());
         query.setParameter("type", type);
 
         query.setFirstResult(itemsPerPage * (page <= 0
@@ -144,7 +145,7 @@ public class JPATaskDAO extends AbstractDAO<Task, Long> implements TaskDAO {
 
     @Override
     public int count(final TaskType type) {
-        Query countQuery = entityManager.createNativeQuery("SELECT COUNT(id) FROM Task WHERE TYPE=?1");
+        Query countQuery = entityManager().createNativeQuery("SELECT COUNT(id) FROM Task WHERE TYPE=?1");
         countQuery.setParameter(1, type.name());
         return ((Number) countQuery.getSingleResult()).intValue();
     }
@@ -152,7 +153,7 @@ public class JPATaskDAO extends AbstractDAO<Task, Long> implements TaskDAO {
     @Transactional(rollbackFor = { Throwable.class })
     @Override
     public <T extends Task> T save(final T task) {
-        return entityManager.merge(task);
+        return entityManager().merge(task);
     }
 
     @Override
@@ -167,7 +168,7 @@ public class JPATaskDAO extends AbstractDAO<Task, Long> implements TaskDAO {
 
     @Override
     public void delete(final Task task) {
-        entityManager.remove(task);
+        entityManager().remove(task);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskExecDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskExecDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskExecDAO.java
index 5d840da..38155ff 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskExecDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskExecDAO.java
@@ -38,11 +38,11 @@ public class JPATaskExecDAO extends AbstractDAO<TaskExec, Long> implements TaskE
 
     @Override
     public TaskExec find(final Long key) {
-        return entityManager.find(JPATaskExec.class, key);
+        return entityManager().find(JPATaskExec.class, key);
     }
 
     private <T extends Task> TaskExec findLatest(final T task, final String field) {
-        TypedQuery<TaskExec> query = entityManager.createQuery(
+        TypedQuery<TaskExec> query = entityManager().createQuery(
                 "SELECT e FROM " + JPATaskExec.class.getSimpleName() + " e "
                 + "WHERE e.task=:task "
                 + "ORDER BY e." + field + " DESC", TaskExec.class);
@@ -71,13 +71,13 @@ public class JPATaskExecDAO extends AbstractDAO<TaskExec, Long> implements TaskE
                 append(" e WHERE e.task IN (").append("SELECT t FROM ").
                 append(taskDAO.getEntityReference(type).getSimpleName()).append(" t)");
 
-        TypedQuery<TaskExec> query = entityManager.createQuery(queryString.toString(), TaskExec.class);
+        TypedQuery<TaskExec> query = entityManager().createQuery(queryString.toString(), TaskExec.class);
         return query.getResultList();
     }
 
     @Override
     public TaskExec save(final TaskExec execution) {
-        return entityManager.merge(execution);
+        return entityManager().merge(execution);
     }
 
     /**
@@ -111,6 +111,6 @@ public class JPATaskExecDAO extends AbstractDAO<TaskExec, Long> implements TaskE
             execution.getTask().removeExec(execution);
         }
 
-        entityManager.remove(execution);
+        entityManager().remove(execution);
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
index 3b413c0..50564f2 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
@@ -29,8 +29,17 @@ import javax.persistence.TypedQuery;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Predicate;
 import org.apache.commons.collections4.Transformer;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.types.AccountPolicySpec;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.Entitlement;
+import org.apache.syncope.common.lib.types.EntityViolationType;
+import org.apache.syncope.common.lib.types.PasswordPolicySpec;
+import org.apache.syncope.core.misc.policy.AccountPolicyEnforcer;
+import org.apache.syncope.core.misc.policy.AccountPolicyException;
+import org.apache.syncope.core.misc.policy.PasswordPolicyEnforcer;
+import org.apache.syncope.core.misc.policy.PolicyEvaluator;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
@@ -40,14 +49,21 @@ import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
 import org.apache.syncope.core.misc.security.AuthContextUtils;
 import org.apache.syncope.core.misc.security.UnauthorizedException;
+import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
+import org.apache.syncope.core.persistence.api.dao.RealmDAO;
 import org.apache.syncope.core.persistence.api.dao.RoleDAO;
+import org.apache.syncope.core.persistence.api.entity.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
+import org.apache.syncope.core.persistence.api.entity.PasswordPolicy;
+import org.apache.syncope.core.persistence.api.entity.Policy;
+import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.Role;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
 import org.apache.syncope.core.persistence.api.entity.user.UMembership;
 import org.apache.syncope.core.persistence.jpa.entity.JPAAnyUtilsFactory;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPADynRoleMembership;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUDynGroupMembership;
+import org.apache.syncope.core.provisioning.api.UserSuspender;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Propagation;
@@ -57,14 +73,32 @@ import org.springframework.transaction.annotation.Transactional;
 public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
 
     @Autowired
+    private RealmDAO realmDAO;
+
+    @Autowired
     private GroupDAO groupDAO;
 
     @Autowired
     private RoleDAO roleDAO;
 
+    @Resource(name = "adminUser")
+    private String adminUser;
+
     @Resource(name = "anonymousUser")
     private String anonymousUser;
 
+    @Autowired
+    private PolicyEvaluator evaluator;
+
+    @Autowired
+    private PasswordPolicyEnforcer ppEnforcer;
+
+    @Autowired
+    private AccountPolicyEnforcer apEnforcer;
+
+    @Autowired(required = false)
+    private UserSuspender suspender;
+
     @Override
     protected AnyUtils init() {
         return new JPAAnyUtilsFactory().getInstance(AnyTypeKind.USER);
@@ -110,7 +144,7 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
 
     @Override
     public User find(final String username) {
-        TypedQuery<User> query = entityManager.createQuery("SELECT e FROM " + JPAUser.class.getSimpleName()
+        TypedQuery<User> query = entityManager().createQuery("SELECT e FROM " + JPAUser.class.getSimpleName()
                 + " e WHERE e.username = :username", User.class);
         query.setParameter("username", username);
 
@@ -126,7 +160,7 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
 
     @Override
     public User findByToken(final String token) {
-        TypedQuery<User> query = entityManager.createQuery("SELECT e FROM " + JPAUser.class.getSimpleName()
+        TypedQuery<User> query = entityManager().createQuery("SELECT e FROM " + JPAUser.class.getSimpleName()
                 + " e WHERE e.token LIKE :token", User.class);
         query.setParameter("token", token);
 
@@ -142,16 +176,151 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
 
     @Override
     public List<User> findBySecurityQuestion(final SecurityQuestion securityQuestion) {
-        TypedQuery<User> query = entityManager.createQuery("SELECT e FROM " + JPAUser.class.getSimpleName()
+        TypedQuery<User> query = entityManager().createQuery("SELECT e FROM " + JPAUser.class.getSimpleName()
                 + " e WHERE e.securityQuestion = :securityQuestion", User.class);
         query.setParameter("securityQuestion", securityQuestion);
 
         return query.getResultList();
     }
 
+    private List<PasswordPolicy> getPasswordPolicies(final User user) {
+        List<PasswordPolicy> policies = new ArrayList<>();
+
+        PasswordPolicy policy;
+
+        // add resource policies
+        for (ExternalResource resource : findAllResources(user)) {
+            policy = resource.getPasswordPolicy();
+            if (policy != null) {
+                policies.add(policy);
+            }
+        }
+
+        // add realm policies
+        for (Realm realm : realmDAO.findAncestors(user.getRealm())) {
+            policy = realm.getPasswordPolicy();
+            if (policy != null) {
+                policies.add(policy);
+            }
+        }
+
+        return policies;
+    }
+
+    private List<AccountPolicy> getAccountPolicies(final User user) {
+        List<AccountPolicy> policies = new ArrayList<>();
+
+        AccountPolicy policy;
+
+        // add resource policies        
+        for (ExternalResource resource : findAllResources(user)) {
+            policy = resource.getAccountPolicy();
+            if (policy != null) {
+                policies.add(policy);
+            }
+        }
+
+        // add realm policies
+        for (Realm realm : realmDAO.findAncestors(user.getRealm())) {
+            policy = realm.getAccountPolicy();
+            if (policy != null) {
+                policies.add(policy);
+            }
+        }
+
+        return policies;
+    }
+
+    private Pair<Boolean, Boolean> enforcePolicies(final User user) {
+        // ------------------------------
+        // Verify password policies
+        // ------------------------------
+        LOG.debug("Password Policy enforcement");
+
+        try {
+            int maxPPSpecHistory = 0;
+            for (Policy policy : getPasswordPolicies(user)) {
+                // evaluate policy
+                PasswordPolicySpec ppSpec = evaluator.evaluate(policy, user);
+                // enforce policy
+                ppEnforcer.enforce(ppSpec, policy.getType(), user);
+
+                if (ppSpec.getHistoryLength() > maxPPSpecHistory) {
+                    maxPPSpecHistory = ppSpec.getHistoryLength();
+                }
+            }
+
+            // update user's password history with encrypted password
+            if (maxPPSpecHistory > 0 && user.getPassword() != null) {
+                user.getPasswordHistory().add(user.getPassword());
+            }
+            // keep only the last maxPPSpecHistory items in user's password history
+            if (maxPPSpecHistory < user.getPasswordHistory().size()) {
+                for (int i = 0; i < user.getPasswordHistory().size() - maxPPSpecHistory; i++) {
+                    user.getPasswordHistory().remove(i);
+                }
+            }
+        } catch (Exception e) {
+            LOG.error("Invalid password for {}", user, e);
+            throw new InvalidEntityException(User.class, EntityViolationType.InvalidPassword, e.getMessage());
+        } finally {
+            // password has been validated, let's remove its clear version
+            user.removeClearPassword();
+        }
+
+        // ------------------------------
+        // Verify account policies
+        // ------------------------------
+        LOG.debug("Account Policy enforcement");
+
+        boolean suspend = false;
+        boolean propagateSuspension = false;
+        try {
+            if (adminUser.equals(user.getUsername()) || anonymousUser.equals(user.getUsername())) {
+                throw new AccountPolicyException("Not allowed: " + user.getUsername());
+            }
+
+            // invalid username
+            for (Policy policy : getAccountPolicies(user)) {
+                // evaluate policy
+                AccountPolicySpec apSpec = evaluator.evaluate(policy, user);
+
+                // enforce policy
+                suspend |= apEnforcer.enforce(apSpec, policy.getType(), user);
+                propagateSuspension |= apSpec.isPropagateSuspension();
+            }
+        } catch (Exception e) {
+            LOG.error("Invalid username for {}", user, e);
+            throw new InvalidEntityException(User.class, EntityViolationType.InvalidUsername, e.getMessage());
+        }
+
+        return ImmutablePair.of(suspend, propagateSuspension);
+    }
+
     @Override
     public User save(final User user) {
+        // 1. save clear password value before save
+        String clearPwd = user.getClearPassword();
+
+        // 2. save and flush to trigger entity validation        
         User merged = super.save(user);
+        entityManager().flush();
+
+        // 3. set back the sole clear password value
+        JPAUser.class.cast(merged).setClearPassword(clearPwd);
+
+        // 4. enforce password and account policies
+        Pair<Boolean, Boolean> enforceSuspend = null;
+        try {
+            enforceSuspend = enforcePolicies(merged);
+        } catch (InvalidEntityException e) {
+            entityManager().remove(merged);
+            throw e;
+        }
+
+        if (suspender != null && enforceSuspend.getKey()) {
+            suspender.suspend(user, enforceSuspend.getValue());
+        }
 
         roleDAO.refreshDynMemberships(merged);
         groupDAO.refreshDynMemberships(merged);
@@ -168,13 +337,13 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
             group.getUDynMembership().remove(user);
         }
 
-        entityManager.remove(user);
+        entityManager().remove(user);
     }
 
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
     @Override
     public List<Role> findDynRoleMemberships(final User user) {
-        TypedQuery<Role> query = entityManager.createQuery(
+        TypedQuery<Role> query = entityManager().createQuery(
                 "SELECT e.role FROM " + JPADynRoleMembership.class.getSimpleName()
                 + " e WHERE :user MEMBER OF e.users", Role.class);
         query.setParameter("user", user);
@@ -185,7 +354,7 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
     @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true)
     @Override
     public List<Group> findDynGroupMemberships(final User user) {
-        TypedQuery<Group> query = entityManager.createQuery(
+        TypedQuery<Group> query = entityManager().createQuery(
                 "SELECT e.group FROM " + JPAUDynGroupMembership.class.getSimpleName()
                 + " e WHERE :user MEMBER OF e.users", Group.class);
         query.setParameter("user", user);

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAVirAttrDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAVirAttrDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAVirAttrDAO.java
index 3ef6791..2c8b99f 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAVirAttrDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAVirAttrDAO.java
@@ -49,19 +49,19 @@ public class JPAVirAttrDAO extends AbstractDAO<VirAttr<?>, Long> implements VirA
 
     @Override
     public <T extends VirAttr<?>> T find(final Long key, final Class<T> reference) {
-        return reference.cast(entityManager.find(getJPAEntityReference(reference), key));
+        return reference.cast(entityManager().find(getJPAEntityReference(reference), key));
     }
 
     @Override
     public <T extends VirAttr<?>> List<T> findAll(final Class<T> reference) {
-        TypedQuery<T> query = entityManager.createQuery(
+        TypedQuery<T> query = entityManager().createQuery(
                 "SELECT e FROM " + getJPAEntityReference(reference).getSimpleName() + " e", reference);
         return query.getResultList();
     }
 
     @Override
     public <T extends VirAttr<?>> T save(final T virAttr) {
-        return entityManager.merge(virAttr);
+        return entityManager().merge(virAttr);
     }
 
     @Override
@@ -81,6 +81,6 @@ public class JPAVirAttrDAO extends AbstractDAO<VirAttr<?>, Long> implements VirA
             ((Any<?, ?, T>) virAttr.getOwner()).remove(virAttr);
         }
 
-        entityManager.remove(virAttr);
+        entityManager().remove(virAttr);
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAVirSchemaDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAVirSchemaDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAVirSchemaDAO.java
index dae6afc..46a06f6 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAVirSchemaDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAVirSchemaDAO.java
@@ -45,7 +45,7 @@ public class JPAVirSchemaDAO extends AbstractDAO<VirSchema, String> implements V
 
     @Override
     public VirSchema find(final String key) {
-        return entityManager.find(JPAVirSchema.class, key);
+        return entityManager().find(JPAVirSchema.class, key);
     }
 
     @Override
@@ -54,7 +54,7 @@ public class JPAVirSchemaDAO extends AbstractDAO<VirSchema, String> implements V
                 append(JPAVirSchema.class.getSimpleName()).
                 append(" e WHERE e.anyTypeClass=:anyTypeClass");
 
-        TypedQuery<VirSchema> query = entityManager.createQuery(queryString.toString(), VirSchema.class);
+        TypedQuery<VirSchema> query = entityManager().createQuery(queryString.toString(), VirSchema.class);
         query.setParameter("anyTypeClass", anyTypeClass);
 
         return query.getResultList();
@@ -62,7 +62,7 @@ public class JPAVirSchemaDAO extends AbstractDAO<VirSchema, String> implements V
 
     @Override
     public List<VirSchema> findAll() {
-        TypedQuery<VirSchema> query = entityManager.createQuery(
+        TypedQuery<VirSchema> query = entityManager().createQuery(
                 "SELECT e FROM " + JPAVirSchema.class.getSimpleName() + " e", VirSchema.class);
         return query.getResultList();
     }
@@ -73,7 +73,7 @@ public class JPAVirSchemaDAO extends AbstractDAO<VirSchema, String> implements V
                 append(((JPAVirAttrDAO) virAttrDAO).getJPAEntityReference(reference).getSimpleName()).
                 append(" e WHERE e.schema=:schema");
 
-        TypedQuery<T> query = entityManager.createQuery(queryString.toString(), reference);
+        TypedQuery<T> query = entityManager().createQuery(queryString.toString(), reference);
         query.setParameter("schema", schema);
 
         return query.getResultList();
@@ -81,7 +81,7 @@ public class JPAVirSchemaDAO extends AbstractDAO<VirSchema, String> implements V
 
     @Override
     public VirSchema save(final VirSchema virSchema) {
-        return entityManager.merge(virSchema);
+        return entityManager().merge(virSchema);
     }
 
     @Override
@@ -106,6 +106,6 @@ public class JPAVirSchemaDAO extends AbstractDAO<VirSchema, String> implements V
             schema.getAnyTypeClass().remove(schema);
         }
 
-        entityManager.remove(schema);
+        entityManager().remove(schema);
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java
index 06bebdb..2913dc6 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/group/JPAGroup.java
@@ -125,7 +125,7 @@ public class JPAGroup extends AbstractAny<GPlainAttr, GDerAttr, GVirAttr> implem
 
     @Override
     public AnyType getType() {
-        return ApplicationContextProvider.getApplicationContext().getBean(AnyTypeDAO.class).findGroup();
+        return ApplicationContextProvider.getBeanFactory().getBean(AnyTypeDAO.class).findGroup();
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractProvisioningTask.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractProvisioningTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractProvisioningTask.java
index 9723f1d..463408e 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractProvisioningTask.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractProvisioningTask.java
@@ -80,15 +80,15 @@ public abstract class AbstractProvisioningTask extends JPASchedTask implements P
     @Enumerated(EnumType.STRING)
     protected MatchingRule matchingRule;
 
-    public AbstractProvisioningTask(final TaskType type, final String jobClassName) {
+    public AbstractProvisioningTask(final TaskType type, final String jobDelegateClassName) {
         super();
 
         this.type = type;
-        super.setJobClassName(jobClassName);
+        super.setJobDelegateClassName(jobDelegateClassName);
     }
 
     @Override
-    public void setJobClassName(final String jobClassName) {
+    public void setJobDelegateClassName(final String jobClassName) {
         // fixed to SyncJob, cannot be changed
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPushTask.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPushTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPushTask.java
index 0592a9d..190cc0f 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPushTask.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPushTask.java
@@ -37,7 +37,6 @@ import org.apache.syncope.common.lib.types.TaskType;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.task.AnyFilter;
 import org.apache.syncope.core.persistence.api.entity.task.PushTask;
-import org.apache.syncope.core.provisioning.api.job.PushJob;
 
 @Entity
 @DiscriminatorValue("PushTask")
@@ -59,7 +58,7 @@ public class JPAPushTask extends AbstractProvisioningTask implements PushTask {
      * Default constructor.
      */
     public JPAPushTask() {
-        super(TaskType.PUSH, PushJob.class.getName());
+        super(TaskType.PUSH, null);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTask.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTask.java
index 5703380..4f650ba 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTask.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTask.java
@@ -34,8 +34,7 @@ public class JPASchedTask extends JPATask implements SchedTask {
 
     protected String cronExpression;
 
-    @NotNull
-    protected String jobClassName;
+    protected String jobDelegateClassName;
 
     @NotNull
     protected String name;
@@ -58,13 +57,13 @@ public class JPASchedTask extends JPATask implements SchedTask {
     }
 
     @Override
-    public String getJobClassName() {
-        return jobClassName;
+    public String getJobDelegateClassName() {
+        return jobDelegateClassName;
     }
 
     @Override
-    public void setJobClassName(final String jobClassName) {
-        this.jobClassName = jobClassName;
+    public void setJobDelegateClassName(final String jobDelegateClassName) {
+        this.jobDelegateClassName = jobDelegateClassName;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASyncTask.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASyncTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASyncTask.java
index 41eb4ea..f2da581 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASyncTask.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASyncTask.java
@@ -40,7 +40,6 @@ import org.apache.commons.collections4.Predicate;
 import org.apache.syncope.common.lib.types.TaskType;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.task.SyncTask;
-import org.apache.syncope.core.provisioning.api.job.SyncJob;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.task.AnyTemplate;
 import org.apache.syncope.core.persistence.jpa.entity.JPARealm;
@@ -73,7 +72,7 @@ public class JPASyncTask extends AbstractProvisioningTask implements SyncTask {
      * Default constructor.
      */
     public JPASyncTask() {
-        super(TaskType.SYNCHRONIZATION, SyncJob.class.getName());
+        super(TaskType.SYNCHRONIZATION, null);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java
index 88c86fb..1f153da 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/user/JPAUser.java
@@ -55,7 +55,6 @@ import org.apache.syncope.core.persistence.api.entity.user.UDerAttr;
 import org.apache.syncope.core.persistence.api.entity.user.UPlainAttr;
 import org.apache.syncope.core.persistence.api.entity.user.UVirAttr;
 import org.apache.syncope.core.persistence.api.entity.user.User;
-import org.apache.syncope.core.persistence.jpa.validation.entity.UserCheck;
 import org.apache.syncope.core.persistence.jpa.entity.resource.JPAExternalResource;
 import org.apache.syncope.core.persistence.jpa.entity.JPASecurityQuestion;
 import org.apache.syncope.core.misc.security.Encryptor;
@@ -75,7 +74,6 @@ import org.apache.syncope.core.persistence.jpa.entity.JPARole;
 @Entity
 @Table(name = JPAUser.TABLE)
 @Cacheable
-@UserCheck
 public class JPAUser extends AbstractAny<UPlainAttr, UDerAttr, UVirAttr> implements User {
 
     private static final long serialVersionUID = -3905046855521446823L;
@@ -212,7 +210,7 @@ public class JPAUser extends AbstractAny<UPlainAttr, UDerAttr, UVirAttr> impleme
 
     @Override
     public AnyType getType() {
-        return ApplicationContextProvider.getApplicationContext().getBean(AnyTypeDAO.class).findUser();
+        return ApplicationContextProvider.getBeanFactory().getBean(AnyTypeDAO.class).findUser();
     }
 
     @Override
@@ -252,9 +250,13 @@ public class JPAUser extends AbstractAny<UPlainAttr, UDerAttr, UVirAttr> impleme
         return clearPassword;
     }
 
+    public void setClearPassword(final String clearPassword) {
+        this.clearPassword = clearPassword;
+    }
+
     @Override
     public void removeClearPassword() {
-        clearPassword = null;
+        setClearPassword(null);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java
new file mode 100644
index 0000000..3e44864
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/CommonEntityManagerFactoryConf.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.spring;
+
+import java.util.HashMap;
+import java.util.Map;
+import javax.persistence.ValidationMode;
+import javax.sql.DataSource;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.core.persistence.api.DomainsHolder;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor;
+
+/**
+ * Container for common configuration options among all EntityManagerFactory entities (one for each domain).
+ * <br/>
+ * Acts as a commodity place for fetching each domain's {@link DataSource}..
+ */
+public class CommonEntityManagerFactoryConf implements DomainsHolder, InitializingBean, ApplicationContextAware {
+
+    private ApplicationContext ctx;
+
+    private final Map<String, DataSource> domains = new HashMap<>();
+
+    private String[] packagesToScan;
+
+    private ValidationMode validationMode;
+
+    private PersistenceUnitPostProcessor[] postProcessors;
+
+    private final Map<String, Object> jpaPropertyMap = new HashMap<>();
+
+    @Override
+    public void setApplicationContext(final ApplicationContext ctx) throws BeansException {
+        this.ctx = ctx;
+    }
+
+    @Override
+    public void afterPropertiesSet() {
+        for (Map.Entry<String, DataSource> entry : ctx.getBeansOfType(DataSource.class).entrySet()) {
+            if (!entry.getKey().startsWith("local")) {
+                this.domains.put(
+                        StringUtils.substringBefore(entry.getKey(), DataSource.class.getSimpleName()),
+                        entry.getValue());
+            }
+        }
+    }
+
+    @Override
+    public Map<String, DataSource> getDomains() {
+        return domains;
+    }
+
+    public String[] getPackagesToScan() {
+        return packagesToScan;
+    }
+
+    public void setPackagesToScan(final String... packagesToScan) {
+        this.packagesToScan = packagesToScan;
+    }
+
+    public ValidationMode getValidationMode() {
+        return validationMode;
+    }
+
+    public void setValidationMode(final ValidationMode validationMode) {
+        this.validationMode = validationMode;
+    }
+
+    public PersistenceUnitPostProcessor[] getPersistenceUnitPostProcessors() {
+        return postProcessors;
+    }
+
+    public void setPersistenceUnitPostProcessors(final PersistenceUnitPostProcessor... postProcessors) {
+        this.postProcessors = postProcessors;
+    }
+
+    public Map<String, ?> getJpaPropertyMap() {
+        return jpaPropertyMap;
+    }
+
+    public void setJpaPropertyMap(final Map<String, ?> jpaProperties) {
+        if (jpaProperties != null) {
+            this.jpaPropertyMap.putAll(jpaProperties);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainEntityManagerFactoryBean.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainEntityManagerFactoryBean.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainEntityManagerFactoryBean.java
new file mode 100644
index 0000000..a959f96
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainEntityManagerFactoryBean.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.spring;
+
+import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
+
+/**
+ * Extension of {@link LocalContainerEntityManagerFactoryBean} relying on {@link CommonEntityManagerFactoryConf} for
+ * common configuration options.
+ */
+public class DomainEntityManagerFactoryBean extends LocalContainerEntityManagerFactoryBean {
+
+    private static final long serialVersionUID = 49152547930966545L;
+
+    public void setCommonEntityManagerFactoryConf(final CommonEntityManagerFactoryConf commonEMFConf) {
+        super.setJpaPropertyMap(commonEMFConf.getJpaPropertyMap());
+
+        if (commonEMFConf.getPackagesToScan() != null) {
+            super.setPackagesToScan(commonEMFConf.getPackagesToScan());
+        }
+
+        super.setValidationMode(commonEMFConf.getValidationMode());
+
+        if (commonEMFConf.getPersistenceUnitPostProcessors() != null) {
+            super.setPersistenceUnitPostProcessors(commonEMFConf.getPersistenceUnitPostProcessors());
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainTransactionInterceptor.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainTransactionInterceptor.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainTransactionInterceptor.java
new file mode 100644
index 0000000..42779ed
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/DomainTransactionInterceptor.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.spring;
+
+import java.lang.reflect.Method;
+import org.aopalliance.intercept.MethodInvocation;
+import org.apache.syncope.core.misc.security.AuthContextUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
+import org.springframework.transaction.interceptor.TransactionAttribute;
+import org.springframework.transaction.interceptor.TransactionAttributeSource;
+import org.springframework.transaction.interceptor.TransactionInterceptor;
+
+/**
+ * Extends the standard {@link TransactionInterceptor} by dynamically setting the appropriate
+ * {@link TransactionAttribute} qualifier according to the authentication domain of the caller - retrieved via
+ * {@link AuthContextUtils#getDomain()}.
+ */
+public class DomainTransactionInterceptor extends TransactionInterceptor {
+
+    private static final long serialVersionUID = 5113728988680448551L;
+
+    private static final Logger LOG = LoggerFactory.getLogger(DomainTransactionInterceptor.class);
+
+    @Override
+    public TransactionAttributeSource getTransactionAttributeSource() {
+        final TransactionAttributeSource origTxAttrSource = super.getTransactionAttributeSource();
+
+        return new TransactionAttributeSource() {
+
+            @Override
+            public TransactionAttribute getTransactionAttribute(final Method method, final Class<?> targetClass) {
+                TransactionAttribute txAttr = origTxAttrSource.getTransactionAttribute(method, targetClass);
+
+                if (txAttr instanceof DefaultTransactionAttribute) {
+                    ((DefaultTransactionAttribute) txAttr).setQualifier(AuthContextUtils.getDomain());
+                }
+
+                return txAttr;
+            }
+        };
+    }
+
+    @Override
+    public Object invoke(final MethodInvocation invocation) throws Throwable {
+        try {
+            return super.invoke(invocation);
+        } catch (Throwable e) {
+            LOG.debug("Error during {} invocation", invocation.getMethod(), e);
+            throw e;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/SpringComponentReplacer.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/SpringComponentReplacer.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/SpringComponentReplacer.java
new file mode 100644
index 0000000..e983426
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/spring/SpringComponentReplacer.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.jpa.spring;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.interceptor.TransactionInterceptor;
+
+/**
+ * Hack for dynamically replacing standard {@link TransactionInterceptor} with
+ * {@link DomainTransactionInterceptor} in Spring context.
+ */
+@Component
+public class SpringComponentReplacer implements BeanFactoryPostProcessor {
+
+    @Override
+    public void postProcessBeanFactory(final ConfigurableListableBeanFactory factory) throws BeansException {
+        for (String name : factory.getBeanNamesForType(TransactionInterceptor.class)) {
+            BeanDefinition bd = factory.getBeanDefinition(name);
+            bd.setBeanClassName(DomainTransactionInterceptor.class.getName());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/EntityValidationListener.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/EntityValidationListener.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/EntityValidationListener.java
index b7616c7..f1df70c 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/EntityValidationListener.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/EntityValidationListener.java
@@ -49,7 +49,7 @@ public class EntityValidationListener {
     @PrePersist
     @PreUpdate
     public void validate(final Object object) {
-        final Validator validator = ApplicationContextProvider.getApplicationContext().getBean(Validator.class);
+        final Validator validator = ApplicationContextProvider.getBeanFactory().getBean(Validator.class);
         Set<ConstraintViolation<Object>> violations = validator.validate(object);
         if (!violations.isEmpty()) {
             LOG.warn("Bean validation errors found: {}", violations);

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ProvisioningTaskValidator.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ProvisioningTaskValidator.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ProvisioningTaskValidator.java
index 2b6e4e0..3c64499 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ProvisioningTaskValidator.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/ProvisioningTaskValidator.java
@@ -37,11 +37,11 @@ public class ProvisioningTaskValidator extends AbstractValidator<ProvisioningTas
     }
 
     @Override
-    public boolean isValid(final ProvisioningTask object, final ConstraintValidatorContext context) {
-        boolean isValid = schedV.isValid(object, context);
+    public boolean isValid(final ProvisioningTask task, final ConstraintValidatorContext context) {
+        boolean isValid = schedV.isValid(task, context);
 
         if (isValid) {
-            isValid = object.getResource() != null;
+            isValid = task.getResource() != null;
             if (!isValid) {
                 LOG.error("Resource is null");
 
@@ -51,15 +51,15 @@ public class ProvisioningTaskValidator extends AbstractValidator<ProvisioningTas
                         addPropertyNode("resource").addConstraintViolation();
             }
 
-            if (!object.getActionsClassNames().isEmpty()) {
-                for (String className : object.getActionsClassNames()) {
+            if (!task.getActionsClassNames().isEmpty()) {
+                for (String className : task.getActionsClassNames()) {
                     Class<?> actionsClass = null;
                     boolean isAssignable = false;
                     try {
                         actionsClass = Class.forName(className);
-                        isAssignable = object instanceof JPASyncTask
+                        isAssignable = task instanceof JPASyncTask
                                 ? SyncActions.class.isAssignableFrom(actionsClass)
-                                : object instanceof JPAPushTask
+                                : task instanceof JPAPushTask
                                         ? PushActions.class.isAssignableFrom(actionsClass)
                                         : false;
                     } catch (Exception e) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SchedTaskValidator.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SchedTaskValidator.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SchedTaskValidator.java
index 50a6d5d..5c159c2 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SchedTaskValidator.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/SchedTaskValidator.java
@@ -21,39 +21,43 @@ package org.apache.syncope.core.persistence.jpa.validation.entity;
 import java.text.ParseException;
 
 import javax.validation.ConstraintValidatorContext;
+import org.apache.commons.lang3.ClassUtils;
 import org.apache.syncope.common.lib.types.EntityViolationType;
+import org.apache.syncope.core.persistence.api.entity.task.ProvisioningTask;
 import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
+import org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate;
 import org.quartz.CronExpression;
-import org.quartz.Job;
 
 public class SchedTaskValidator extends AbstractValidator<SchedTaskCheck, SchedTask> {
 
     @Override
-    public boolean isValid(final SchedTask object, final ConstraintValidatorContext context) {
-        boolean isValid;
+    public boolean isValid(final SchedTask task, final ConstraintValidatorContext context) {
+        boolean isValid = true;
 
-        Class<?> jobClass = null;
-        try {
-            jobClass = Class.forName(object.getJobClassName());
-            isValid = Job.class.isAssignableFrom(jobClass);
-        } catch (Exception e) {
-            LOG.error("Invalid Job class specified", e);
-            isValid = false;
-        }
-        if (jobClass == null || !isValid) {
-            isValid = false;
+        if (!(task instanceof ProvisioningTask)) {
+            Class<?> jobDelegateClass = null;
+            try {
+                jobDelegateClass = ClassUtils.getClass(task.getJobDelegateClassName());
+                isValid = SchedTaskJobDelegate.class.isAssignableFrom(jobDelegateClass);
+            } catch (Exception e) {
+                LOG.error("Invalid JobDelegate class specified", e);
+                isValid = false;
+            }
+            if (jobDelegateClass == null || !isValid) {
+                isValid = false;
 
-            context.disableDefaultConstraintViolation();
-            context.buildConstraintViolationWithTemplate(
-                    getTemplate(EntityViolationType.InvalidSchedTask, "Invalid job class name")).
-                    addPropertyNode("jobClassName").addConstraintViolation();
+                context.disableDefaultConstraintViolation();
+                context.buildConstraintViolationWithTemplate(
+                        getTemplate(EntityViolationType.InvalidSchedTask, "Invalid job delegate class name")).
+                        addPropertyNode("jobDelegateClassName").addConstraintViolation();
+            }
         }
 
-        if (isValid && object.getCronExpression() != null) {
+        if (isValid && task.getCronExpression() != null) {
             try {
-                new CronExpression(object.getCronExpression());
+                new CronExpression(task.getCronExpression());
             } catch (ParseException e) {
-                LOG.error("Invalid cron expression '" + object.getCronExpression() + "'", e);
+                LOG.error("Invalid cron expression '" + task.getCronExpression() + "'", e);
                 isValid = false;
 
                 context.disableDefaultConstraintViolation();

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/UserCheck.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/UserCheck.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/UserCheck.java
deleted file mode 100644
index 2d9fec6..0000000
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/UserCheck.java
+++ /dev/null
@@ -1,41 +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.jpa.validation.entity;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-import javax.validation.Constraint;
-import javax.validation.Payload;
-
-@Target({ ElementType.TYPE })
-@Retention(RetentionPolicy.RUNTIME)
-@Constraint(validatedBy = UserValidator.class)
-@Documented
-public @interface UserCheck {
-
-    String message() default "{org.apache.syncope.core.persistence.validation.user}";
-
-    Class<?>[] groups() default {};
-
-    Class<? extends Payload>[] payload() default {};
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/UserValidator.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/UserValidator.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/UserValidator.java
deleted file mode 100644
index e142de3..0000000
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/UserValidator.java
+++ /dev/null
@@ -1,199 +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.jpa.validation.entity;
-
-import java.util.ArrayList;
-import java.util.List;
-import javax.annotation.Resource;
-import javax.validation.ConstraintValidatorContext;
-import org.apache.syncope.common.lib.types.AccountPolicySpec;
-import org.apache.syncope.common.lib.types.EntityViolationType;
-import org.apache.syncope.common.lib.types.PasswordPolicySpec;
-import org.apache.syncope.core.persistence.api.entity.AccountPolicy;
-import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
-import org.apache.syncope.core.persistence.api.entity.PasswordPolicy;
-import org.apache.syncope.core.persistence.api.entity.Policy;
-import org.apache.syncope.core.persistence.api.entity.user.User;
-import org.apache.syncope.core.misc.policy.AccountPolicyEnforcer;
-import org.apache.syncope.core.misc.policy.AccountPolicyException;
-import org.apache.syncope.core.misc.policy.PasswordPolicyEnforcer;
-import org.apache.syncope.core.misc.policy.PolicyEvaluator;
-import org.apache.syncope.core.persistence.api.dao.RealmDAO;
-import org.apache.syncope.core.persistence.api.dao.UserDAO;
-import org.apache.syncope.core.persistence.api.entity.Realm;
-import org.springframework.beans.factory.annotation.Autowired;
-
-public class UserValidator extends AbstractValidator<UserCheck, User> {
-
-    @Resource(name = "adminUser")
-    private String adminUser;
-
-    @Resource(name = "anonymousUser")
-    private String anonymousUser;
-
-    @Autowired
-    private UserDAO userDAO;
-
-    @Autowired
-    private RealmDAO realmDAO;
-
-    @Autowired
-    private PolicyEvaluator evaluator;
-
-    @Autowired
-    private PasswordPolicyEnforcer ppEnforcer;
-
-    @Autowired
-    private AccountPolicyEnforcer apEnforcer;
-
-    @Override
-    public boolean isValid(final User user, final ConstraintValidatorContext context) {
-        context.disableDefaultConstraintViolation();
-
-        // need to treat it explicitly, otherwise policy evaluation will silently fail
-        if (user.getRealm() == null) {
-            context.buildConstraintViolationWithTemplate(
-                    getTemplate(EntityViolationType.InvalidRealm, "realm not specified")).
-                    addPropertyNode("realm").addConstraintViolation();
-
-            return false;
-        }
-
-        // ------------------------------
-        // Verify password policies
-        // ------------------------------
-        LOG.debug("Password Policy enforcement");
-
-        try {
-            int maxPPSpecHistory = 0;
-            for (Policy policy : getPasswordPolicies(user)) {
-                // evaluate policy
-                PasswordPolicySpec ppSpec = evaluator.evaluate(policy, user);
-                // enforce policy
-                ppEnforcer.enforce(ppSpec, policy.getType(), user);
-
-                if (ppSpec.getHistoryLength() > maxPPSpecHistory) {
-                    maxPPSpecHistory = ppSpec.getHistoryLength();
-                }
-            }
-
-            // update user's password history with encrypted password
-            if (maxPPSpecHistory > 0 && user.getPassword() != null) {
-                user.getPasswordHistory().add(user.getPassword());
-            }
-            // keep only the last maxPPSpecHistory items in user's password history
-            if (maxPPSpecHistory < user.getPasswordHistory().size()) {
-                for (int i = 0; i < user.getPasswordHistory().size() - maxPPSpecHistory; i++) {
-                    user.getPasswordHistory().remove(i);
-                }
-            }
-        } catch (Exception e) {
-            LOG.debug("Invalid password");
-
-            context.buildConstraintViolationWithTemplate(
-                    getTemplate(EntityViolationType.InvalidPassword, e.getMessage())).
-                    addPropertyNode("password").addConstraintViolation();
-
-            return false;
-        } finally {
-            // password has been validated, let's remove its clear version
-            user.removeClearPassword();
-        }
-        // ------------------------------
-
-        // ------------------------------
-        // Verify account policies
-        // ------------------------------
-        LOG.debug("Account Policy enforcement");
-
-        try {
-            if (adminUser.equals(user.getUsername()) || anonymousUser.equals(user.getUsername())) {
-                throw new AccountPolicyException("Not allowed: " + user.getUsername());
-            }
-
-            // invalid username
-            for (Policy policy : getAccountPolicies(user)) {
-                // evaluate policy
-                AccountPolicySpec accountPolicy = evaluator.evaluate(policy, user);
-
-                // enforce policy
-                apEnforcer.enforce(accountPolicy, policy.getType(), user);
-            }
-        } catch (Exception e) {
-            LOG.debug("Invalid username");
-
-            context.buildConstraintViolationWithTemplate(
-                    getTemplate(EntityViolationType.InvalidUsername, e.getMessage())).
-                    addPropertyNode("username").addConstraintViolation();
-
-            return false;
-        }
-        // ------------------------------
-
-        return true;
-    }
-
-    private List<PasswordPolicy> getPasswordPolicies(final User user) {
-        List<PasswordPolicy> policies = new ArrayList<>();
-
-        PasswordPolicy policy;
-
-        // add resource policies
-        for (ExternalResource resource : userDAO.findAllResources(user)) {
-            policy = resource.getPasswordPolicy();
-            if (policy != null) {
-                policies.add(policy);
-            }
-        }
-
-        // add realm policies
-        for (Realm realm : realmDAO.findAncestors(user.getRealm())) {
-            policy = realm.getPasswordPolicy();
-            if (policy != null) {
-                policies.add(policy);
-            }
-        }
-
-        return policies;
-    }
-
-    private List<AccountPolicy> getAccountPolicies(final User user) {
-        List<AccountPolicy> policies = new ArrayList<>();
-
-        AccountPolicy policy;
-
-        // add resource policies
-        for (ExternalResource resource : userDAO.findAllResources(user)) {
-            policy = resource.getAccountPolicy();
-            if (policy != null) {
-                policies.add(policy);
-            }
-        }
-
-        // add realm policies
-        for (Realm realm : realmDAO.findAncestors(user.getRealm())) {
-            policy = realm.getAccountPolicy();
-            if (policy != null) {
-                policies.add(policy);
-            }
-        }
-
-        return policies;
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/resources/audit/audit.sql
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/audit/audit.sql b/core/persistence-jpa/src/main/resources/audit/audit.sql
index faf8c5b..44ffb17 100644
--- a/core/persistence-jpa/src/main/resources/audit/audit.sql
+++ b/core/persistence-jpa/src/main/resources/audit/audit.sql
@@ -21,4 +21,6 @@ CREATE TABLE IF NOT EXISTS SYNCOPEAUDIT (
   LOGGER VARCHAR(255) NOT NULL,
   MESSAGE TEXT NOT NULL,
   THROWABLE TEXT
-)
+);
+
+COMMIT;

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/resources/content.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/content.xml b/core/persistence-jpa/src/main/resources/content.xml
deleted file mode 100644
index caba10e..0000000
--- a/core/persistence-jpa/src/main/resources/content.xml
+++ /dev/null
@@ -1,117 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License.  You may obtain a copy of the License at
-
-  http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing,
-software distributed under the License is distributed on an
-"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, either express or implied.  See the License for the
-specific language governing permissions and limitations
-under the License.
--->
-<dataset>
-  <Realm id="1" name="/"/>
-
-  <SyncopeConf id="1" 
-               creator="admin" lastModifier="admin"
-               creationDate="2014-06-20 11:00:00" lastChangeDate="2014-06-20 11:00:00"/>
-
-  <PlainSchema name="password.cipher.algorithm" type="String"
-               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
-  <CPlainAttr id="1" owner_id="1" schema_name="password.cipher.algorithm"/>
-  <CPlainAttrValue id="1" attribute_id="1" stringValue="SHA1"/>
-
-  <!-- notificationjob.cronExpression:
-  + not existing: NotificationJob runs according to Notification.DEFAULT_CRON_EXP
-  + provided as empty string: NotificationJob disabled
-  + provided as non-empty string: NotificationJob runs according to the given value -->
-  <PlainSchema name="notificationjob.cronExpression" type="String"
-               mandatoryCondition="false" multivalue="0" uniqueConstraint="0" readonly="0"/>
-  <CPlainAttr id="2" owner_id="1" schema_name="notificationjob.cronExpression"/>
-  <CPlainAttrValue id="2" attribute_id="2" stringValue=""/>
-
-  <PlainSchema name="notification.maxRetries" type="Long"
-               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
-  <CPlainAttr id="3" owner_id="1" schema_name="notification.maxRetries"/>
-  <CPlainAttrValue id="3" attribute_id="3" longValue="3"/>
-
-  <PlainSchema name="token.length" type="Long"
-               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
-  <CPlainAttr id="4" owner_id="1" schema_name="token.length"/>
-  <CPlainAttrValue id="4" attribute_id="4" longValue="256"/>
-
-  <PlainSchema name="token.expireTime" type="Long"
-               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
-  <CPlainAttr id="5" owner_id="1" schema_name="token.expireTime"/>
-  <CPlainAttrValue id="5" attribute_id="5" longValue="60"/>
-
-  <PlainSchema name="selfRegistration.allowed" type="Boolean"
-               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
-  <CPlainAttr id="6" owner_id="1" schema_name="selfRegistration.allowed"/>
-  <CPlainAttrValue id="6" attribute_id="6" booleanValue="1"/>
-
-  <PlainSchema name="passwordReset.allowed" type="Boolean"
-               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
-  <CPlainAttr id="7" owner_id="1" schema_name="passwordReset.allowed"/>
-  <CPlainAttrValue id="7" attribute_id="7" booleanValue="1"/>
-
-  <PlainSchema name="passwordReset.securityQuestion" type="Boolean"
-               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
-  <CPlainAttr id="8" owner_id="1" schema_name="passwordReset.securityQuestion"/>
-  <CPlainAttrValue id="8" attribute_id="8" booleanValue="1"/>
-
-  <PlainSchema name="authentication.statuses" type="String"
-               mandatoryCondition="true" multivalue="1" uniqueConstraint="0" readonly="0"/>
-  <CPlainAttr id="9" owner_id="1" schema_name="authentication.statuses"/>
-  <CPlainAttrValue id="9" attribute_id="9" stringValue="created"/>
-  <CPlainAttrValue id="10" attribute_id="9" stringValue="active"/>
-
-  <!-- Save user login date upon successful authentication -->
-  <PlainSchema name="log.lastlogindate" type="Boolean"
-               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
-  <CPlainAttr id="11" owner_id="1" schema_name="log.lastlogindate"/>
-  <CPlainAttrValue id="11" attribute_id="11" booleanValue="1"/>
-
-  <PlainSchema name="tasks.interruptMaxRetries" type="Long"
-               mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
-  <CPlainAttr id="12" owner_id="1" schema_name="tasks.interruptMaxRetries"/>
-  <CPlainAttrValue id="12" attribute_id="12" longValue="20"/>
-
-  <!-- For usage with admin console -->
-  <PlainSchema name="admin.user.layout" type="String"
-               mandatoryCondition="false" multivalue="1" uniqueConstraint="0" readonly="0"/>
-  <PlainSchema name="self.user.layout" type="String"
-               mandatoryCondition="false" multivalue="1" uniqueConstraint="0" readonly="0"/>
-  <PlainSchema name="admin.group.layout" type="String"
-               mandatoryCondition="false" multivalue="1" uniqueConstraint="0" readonly="0"/>
-  <PlainSchema name="self.group.layout" type="String"
-               mandatoryCondition="false" multivalue="1" uniqueConstraint="0" readonly="0"/>
-  <PlainSchema name="admin.membership.layout" type="String"
-               mandatoryCondition="false" multivalue="1" uniqueConstraint="0" readonly="0"/>
-  <PlainSchema name="self.membership.layout" type="String"
-               mandatoryCondition="false" multivalue="1" uniqueConstraint="0" readonly="0"/>
-        
-  <PlainSchema name="email" type="String"
-               mandatoryCondition="false" multivalue="0" uniqueConstraint="0" readonly="0"
-               validatorClass="org.apache.syncope.core.persistence.jpa.attrvalue.validation.EmailAddressValidator"/>
-  
-  <!-- Password reset notifications -->
-  <Notification id="1" active="1" recipientAttrName="email" recipientAttrType="UserPlainSchema" selfAsRecipient="1" 
-                sender="admin@syncope.apache.org" subject="Password Reset request" template="requestPasswordReset" 
-                traceLevel="FAILURES" userAbout="token!=$null"/> 
-  <Notification_events Notification_id="1" event="[CUSTOM]:[]:[]:[requestPasswordReset]:[SUCCESS]"/>
-  
-  <Notification id="2" active="1" recipientAttrName="email" recipientAttrType="UserPlainSchema" selfAsRecipient="1" 
-                sender="admin@syncope.apache.org" subject="Password Reset successful" template="confirmPasswordReset" 
-                traceLevel="FAILURES" userAbout="token!=$null"/> 
-  <Notification_events Notification_id="2" event="[CUSTOM]:[]:[]:[confirmPasswordReset]:[SUCCESS]"/>
-
-</dataset>

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/resources/domains.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/domains.xml b/core/persistence-jpa/src/main/resources/domains.xml
new file mode 100644
index 0000000..43fce14
--- /dev/null
+++ b/core/persistence-jpa/src/main/resources/domains.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+                           http://www.springframework.org/schema/beans/spring-beans.xsd">
+  
+  <import resource="classpath*:domains/*Domain.xml"/>
+    
+  <bean id="commonEMFConf" class="org.apache.syncope.core.persistence.jpa.spring.CommonEntityManagerFactoryConf">
+    <property name="packagesToScan" value="org.apache.syncope.core.persistence.jpa.entity"/>
+    <property name="validationMode" value="NONE"/>
+    <property name="persistenceUnitPostProcessors">
+      <list>
+        <bean class="org.apache.syncope.core.persistence.jpa.spring.MultiJarAwarePersistenceUnitPostProcessor"/>
+      </list>
+    </property>
+    <property name="jpaPropertyMap">
+      <map>
+        <entry key="openjpa.Log" value="slf4j"/>
+        <!--<entry key="openjpa.Log" value="SQL=TRACE"/>
+        <entry key="openjpa.ConnectionFactoryProperties" 
+        value="PrintParameters=true, PrettyPrint=true, PrettyPrintLineLength=80"/>-->
+                                
+        <entry key="openjpa.NontransactionalWrite" value="false"/>
+        <entry key="openjpa.AutoDetach" value="close, commit, nontx-read, rollback"/>
+
+        <entry key="openjpa.jdbc.SchemaFactory" value="native(ForeignKeys=true)"/>
+        <entry key="openjpa.jdbc.MappingDefaults" 
+               value="ForeignKeyDeleteAction=restrict, JoinForeignKeyDeleteAction=restrict"/>
+                
+        <entry key="openjpa.DataCache" value="true"/>
+        <entry key="openjpa.QueryCache" value="true"/>
+        <entry key="openjpa.RemoteCommitProvider" value="sjvm"/>
+      </map>
+    </property>    
+  </bean>
+  
+</beans>

http://git-wip-us.apache.org/repos/asf/syncope/blob/34a1c214/core/persistence-jpa/src/main/resources/domains/Master.properties
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/domains/Master.properties b/core/persistence-jpa/src/main/resources/domains/Master.properties
new file mode 100644
index 0000000..177e988
--- /dev/null
+++ b/core/persistence-jpa/src/main/resources/domains/Master.properties
@@ -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.
+Master.driverClassName=org.postgresql.Driver
+Master.url=jdbc:postgresql://localhost:5432/syncope
+Master.schema=
+Master.username=syncope
+Master.password=syncope
+Master.databasePlatform=org.apache.openjpa.jdbc.sql.PostgresDictionary
+Master.orm=META-INF/spring-orm.xml
+
+# note: other connection pool settings can also be configured here, see DataSource definition
+Master.pool.validationQuery=SELECT 1
+
+Master.audit.sql=audit.sql