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/01/12 17:31:43 UTC

[04/52] [abbrv] [partial] syncope git commit: [SYNCOPE-620] Unit tests all in

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/SyncJobImpl.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/SyncJobImpl.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/SyncJobImpl.java
new file mode 100644
index 0000000..c47bff6
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/SyncJobImpl.java
@@ -0,0 +1,221 @@
+/*
+ * 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.server.provisioning.java.sync;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.mod.ReferenceMod;
+import org.apache.syncope.common.lib.mod.RoleMod;
+import org.apache.syncope.common.lib.types.SyncPolicySpec;
+import org.apache.syncope.server.persistence.api.dao.NotFoundException;
+import org.apache.syncope.server.persistence.api.entity.ExternalResource;
+import org.apache.syncope.server.persistence.api.entity.SyncPolicy;
+import org.apache.syncope.server.persistence.api.entity.role.RMapping;
+import org.apache.syncope.server.persistence.api.entity.task.ProvisioningTask;
+import org.apache.syncope.server.persistence.api.entity.task.SyncTask;
+import org.apache.syncope.server.persistence.api.entity.user.UMapping;
+import org.apache.syncope.server.provisioning.api.Connector;
+import org.apache.syncope.server.provisioning.api.sync.ProvisioningProfile;
+import org.apache.syncope.server.provisioning.api.sync.ProvisioningResult;
+import org.apache.syncope.server.provisioning.api.sync.SyncActions;
+import org.apache.syncope.server.misc.security.UnauthorizedRoleException;
+import org.apache.syncope.server.misc.spring.ApplicationContextProvider;
+import org.apache.syncope.server.provisioning.api.job.SyncJob;
+import org.apache.syncope.server.workflow.api.RoleWorkflowAdapter;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.identityconnectors.framework.common.objects.SyncToken;
+import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+
+/**
+ * Job for executing synchronization (from external resource) tasks.
+ *
+ * @see AbstractProvisioningJob
+ * @see SyncTask
+ */
+public class SyncJobImpl extends AbstractProvisioningJob<SyncTask, SyncActions> implements SyncJob {
+
+    /**
+     * Role workflow adapter.
+     */
+    @Autowired
+    private RoleWorkflowAdapter rwfAdapter;
+
+    @Autowired
+    protected SyncUtilities syncUtilities;
+
+    protected void setRoleOwners(final RoleSyncResultHandler rhandler)
+            throws UnauthorizedRoleException, NotFoundException {
+
+        for (Map.Entry<Long, String> entry : rhandler.getRoleOwnerMap().entrySet()) {
+            RoleMod roleMod = new RoleMod();
+            roleMod.setKey(entry.getKey());
+
+            if (StringUtils.isBlank(entry.getValue())) {
+                roleMod.setRoleOwner(null);
+                roleMod.setUserOwner(null);
+            } else {
+                Long userId = syncUtilities.findMatchingAttributableId(
+                        ObjectClass.ACCOUNT,
+                        entry.getValue(),
+                        rhandler.getProfile().getTask().getResource(),
+                        rhandler.getProfile().getConnector());
+
+                if (userId == null) {
+                    Long roleId = syncUtilities.findMatchingAttributableId(
+                            ObjectClass.GROUP,
+                            entry.getValue(),
+                            rhandler.getProfile().getTask().getResource(),
+                            rhandler.getProfile().getConnector());
+
+                    if (roleId != null) {
+                        roleMod.setRoleOwner(new ReferenceMod(roleId));
+                    }
+                } else {
+                    roleMod.setUserOwner(new ReferenceMod(userId));
+                }
+            }
+
+            rwfAdapter.update(roleMod);
+        }
+    }
+
+    @Override
+    protected String executeWithSecurityContext(
+            final SyncTask syncTask,
+            final Connector connector,
+            final UMapping uMapping,
+            final RMapping rMapping,
+            final boolean dryRun) throws JobExecutionException {
+
+        LOG.debug("Execute synchronization with token {}", syncTask.getResource().getUsyncToken());
+
+        final List<ProvisioningResult> results = new ArrayList<>();
+
+        final ProvisioningProfile<SyncTask, SyncActions> profile = new ProvisioningProfile<>(connector, syncTask);
+        profile.getActions().addAll(actions);
+        profile.setDryRun(dryRun);
+        profile.setResAct(getSyncPolicySpec(syncTask).getConflictResolutionAction());
+        profile.getResults().addAll(results);
+
+        // Prepare handler for SyncDelta objects (users)
+        final UserSyncResultHandler uhandler =
+                (UserSyncResultHandler) ApplicationContextProvider.getApplicationContext().getBeanFactory().createBean(
+                        UserSyncResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+        uhandler.setProfile(profile);
+
+        // Prepare handler for SyncDelta objects (roles/groups)
+        final RoleSyncResultHandler rhandler =
+                (RoleSyncResultHandler) ApplicationContextProvider.getApplicationContext().getBeanFactory().createBean(
+                        RoleSyncResultHandler.class, AbstractBeanDefinition.AUTOWIRE_BY_NAME, false);
+        rhandler.setProfile(profile);
+
+        if (!profile.isDryRun()) {
+            for (SyncActions action : actions) {
+                action.beforeAll(profile);
+            }
+        }
+
+        try {
+            SyncToken latestUSyncToken = null;
+            if (uMapping != null && !syncTask.isFullReconciliation()) {
+                latestUSyncToken = connector.getLatestSyncToken(ObjectClass.ACCOUNT);
+            }
+            SyncToken latestRSyncToken = null;
+            if (rMapping != null && !syncTask.isFullReconciliation()) {
+                latestRSyncToken = connector.getLatestSyncToken(ObjectClass.GROUP);
+            }
+
+            if (syncTask.isFullReconciliation()) {
+                if (uMapping != null) {
+                    connector.getAllObjects(ObjectClass.ACCOUNT, uhandler,
+                            connector.getOperationOptions(uMapping.getItems()));
+                }
+                if (rMapping != null) {
+                    connector.getAllObjects(ObjectClass.GROUP, rhandler,
+                            connector.getOperationOptions(rMapping.getItems()));
+                }
+            } else {
+                if (uMapping != null) {
+                    connector.sync(ObjectClass.ACCOUNT, syncTask.getResource().getUsyncToken(), uhandler,
+                            connector.getOperationOptions(uMapping.getItems()));
+                }
+                if (rMapping != null) {
+                    connector.sync(ObjectClass.GROUP, syncTask.getResource().getRsyncToken(), rhandler,
+                            connector.getOperationOptions(rMapping.getItems()));
+                }
+            }
+
+            if (!dryRun && !syncTask.isFullReconciliation()) {
+                try {
+                    ExternalResource resource = resourceDAO.find(syncTask.getResource().getKey());
+                    if (uMapping != null) {
+                        resource.setUsyncToken(latestUSyncToken);
+                    }
+                    if (rMapping != null) {
+                        resource.setRsyncToken(latestRSyncToken);
+                    }
+                    resourceDAO.save(resource);
+                } catch (Exception e) {
+                    throw new JobExecutionException("While updating SyncToken", e);
+                }
+            }
+        } catch (Exception e) {
+            throw new JobExecutionException("While syncing on connector", e);
+        }
+
+        try {
+            setRoleOwners(rhandler);
+        } catch (Exception e) {
+            LOG.error("While setting role owners", e);
+        }
+
+        if (!profile.isDryRun()) {
+            for (SyncActions action : actions) {
+                action.afterAll(profile, results);
+            }
+        }
+
+        final String result = createReport(results, syncTask.getResource().getSyncTraceLevel(), dryRun);
+
+        LOG.debug("Sync result: {}", result);
+
+        return result;
+    }
+
+    private SyncPolicySpec getSyncPolicySpec(final ProvisioningTask task) {
+        SyncPolicySpec syncPolicySpec;
+
+        if (task instanceof SyncTask) {
+            final SyncPolicy syncPolicy = task.getResource().getSyncPolicy() == null
+                    ? policyDAO.getGlobalSyncPolicy()
+                    : task.getResource().getSyncPolicy();
+
+            syncPolicySpec = syncPolicy == null ? null : syncPolicy.getSpecification(SyncPolicySpec.class);
+        } else {
+            syncPolicySpec = null;
+        }
+
+        // step required because the call <policy>.getSpecification() could return a null value
+        return syncPolicySpec == null ? new SyncPolicySpec() : syncPolicySpec;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/SyncUtilities.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/SyncUtilities.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/SyncUtilities.java
new file mode 100644
index 0000000..0d62487
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/SyncUtilities.java
@@ -0,0 +1,413 @@
+/*
+ * 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.server.provisioning.java.sync;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.syncope.common.lib.types.SubjectType;
+import org.apache.syncope.common.lib.types.SyncPolicySpec;
+import org.apache.syncope.server.persistence.api.RoleEntitlementUtil;
+import org.apache.syncope.server.persistence.api.attrvalue.validation.ParsingValidationException;
+import org.apache.syncope.server.persistence.api.dao.EntitlementDAO;
+import org.apache.syncope.server.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.server.persistence.api.dao.PolicyDAO;
+import org.apache.syncope.server.persistence.api.dao.RoleDAO;
+import org.apache.syncope.server.persistence.api.dao.SubjectSearchDAO;
+import org.apache.syncope.server.persistence.api.dao.UserDAO;
+import org.apache.syncope.server.persistence.api.dao.search.AttributeCond;
+import org.apache.syncope.server.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.server.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.server.persistence.api.dao.search.SubjectCond;
+import org.apache.syncope.server.persistence.api.entity.AttributableUtil;
+import org.apache.syncope.server.persistence.api.entity.AttributableUtilFactory;
+import org.apache.syncope.server.persistence.api.entity.ExternalResource;
+import org.apache.syncope.server.persistence.api.entity.MappingItem;
+import org.apache.syncope.server.persistence.api.entity.PlainAttrValue;
+import org.apache.syncope.server.persistence.api.entity.PlainSchema;
+import org.apache.syncope.server.persistence.api.entity.Subject;
+import org.apache.syncope.server.persistence.api.entity.SyncPolicy;
+import org.apache.syncope.server.persistence.api.entity.role.Role;
+import org.apache.syncope.server.persistence.api.entity.task.ProvisioningTask;
+import org.apache.syncope.server.persistence.api.entity.user.UDerAttr;
+import org.apache.syncope.server.persistence.api.entity.user.UPlainAttr;
+import org.apache.syncope.server.persistence.api.entity.user.UVirAttr;
+import org.apache.syncope.server.persistence.api.entity.user.User;
+import org.apache.syncope.server.provisioning.api.Connector;
+import org.apache.syncope.server.provisioning.api.sync.SyncCorrelationRule;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.AttributeUtil;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.Name;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.identityconnectors.framework.common.objects.OperationalAttributes;
+import org.identityconnectors.framework.common.objects.filter.EqualsFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class SyncUtilities {
+
+    /**
+     * Logger.
+     */
+    protected static final Logger LOG = LoggerFactory.getLogger(SyncUtilities.class);
+
+    /**
+     * Policy DAO.
+     */
+    @Autowired
+    protected PolicyDAO policyDAO;
+
+    /**
+     * Entitlement DAO.
+     */
+    @Autowired
+    protected EntitlementDAO entitlementDAO;
+
+    /**
+     * Schema DAO.
+     */
+    @Autowired
+    protected PlainSchemaDAO plainSchemaDAO;
+
+    /**
+     * User DAO.
+     */
+    @Autowired
+    protected UserDAO userDAO;
+
+    /**
+     * Role DAO.
+     */
+    @Autowired
+    protected RoleDAO roleDAO;
+
+    /**
+     * Search DAO.
+     */
+    @Autowired
+    protected SubjectSearchDAO searchDAO;
+
+    @Autowired
+    protected AttributableUtilFactory attrUtilFactory;
+
+    public Long findMatchingAttributableId(
+            final ObjectClass oclass,
+            final String name,
+            final ExternalResource resource,
+            final Connector connector) {
+        Long result = null;
+
+        final AttributableUtil attrUtil = attrUtilFactory.getInstance(oclass);
+
+        final List<ConnectorObject> found = connector.search(oclass,
+                new EqualsFilter(new Name(name)), connector.getOperationOptions(
+                        attrUtil.getMappingItems(resource, MappingPurpose.SYNCHRONIZATION)));
+
+        if (found.isEmpty()) {
+            LOG.debug("No {} found on {} with __NAME__ {}", oclass, resource, name);
+        } else {
+            if (found.size() > 1) {
+                LOG.warn("More than one {} found on {} with __NAME__ {} - taking first only", oclass, resource, name);
+            }
+
+            ConnectorObject connObj = found.iterator().next();
+            try {
+                final List<Long> subjectIds = findExisting(connObj.getUid().getUidValue(), connObj, resource, attrUtil);
+                if (subjectIds.isEmpty()) {
+                    LOG.debug("No matching {} found for {}, aborting", attrUtil.getType(), connObj);
+                } else {
+                    if (subjectIds.size() > 1) {
+                        LOG.warn("More than one {} found {} - taking first only", attrUtil.getType(), subjectIds);
+                    }
+
+                    result = subjectIds.iterator().next();
+                }
+            } catch (IllegalArgumentException e) {
+                LOG.warn(e.getMessage());
+            }
+        }
+
+        return result;
+    }
+
+    public List<Long> findByAccountIdItem(
+            final String uid, final ExternalResource resource, final AttributableUtil attrUtil) {
+        final List<Long> result = new ArrayList<>();
+
+        final MappingItem accountIdItem = attrUtil.getAccountIdItem(resource);
+        switch (accountIdItem.getIntMappingType()) {
+            case UserSchema:
+            case RoleSchema:
+                final PlainAttrValue value = attrUtil.newPlainAttrValue();
+
+                PlainSchema schema = plainSchemaDAO.find(accountIdItem.getIntAttrName(), attrUtil.plainSchemaClass());
+                if (schema == null) {
+                    value.setStringValue(uid);
+                } else {
+                    try {
+                        value.parseValue(schema, uid);
+                    } catch (ParsingValidationException e) {
+                        LOG.error("While parsing provided __UID__ {}", uid, e);
+                        value.setStringValue(uid);
+                    }
+                }
+
+                List<? extends Subject<UPlainAttr, UDerAttr, UVirAttr>> users =
+                        userDAO.findByAttrValue(accountIdItem.getIntAttrName(), value, attrUtil);
+                for (Subject<UPlainAttr, UDerAttr, UVirAttr> subject : users) {
+                    result.add(subject.getKey());
+                }
+                break;
+
+            case UserDerivedSchema:
+            case RoleDerivedSchema:
+                users = userDAO.findByDerAttrValue(accountIdItem.getIntAttrName(), uid, attrUtil);
+                for (Subject<UPlainAttr, UDerAttr, UVirAttr> subject : users) {
+                    result.add(subject.getKey());
+                }
+                break;
+
+            case Username:
+                User user = userDAO.find(uid);
+                if (user != null) {
+                    result.add(user.getKey());
+                }
+                break;
+
+            case UserId:
+                user = userDAO.find(Long.parseLong(uid));
+                if (user != null) {
+                    result.add(user.getKey());
+                }
+                break;
+
+            case RoleName:
+                List<Role> roles = roleDAO.find(uid);
+                for (Role role : roles) {
+                    result.add(role.getKey());
+                }
+                break;
+
+            case RoleId:
+                Role role = roleDAO.find(Long.parseLong(uid));
+                if (role != null) {
+                    result.add(role.getKey());
+                }
+                break;
+
+            default:
+                LOG.error("Invalid accountId type '{}'", accountIdItem.getIntMappingType());
+        }
+
+        return result;
+    }
+
+    public List<Long> search(final SearchCond searchCond, final SubjectType type) {
+        final List<Long> result = new ArrayList<>();
+
+        List<Subject<?, ?, ?>> subjects = searchDAO.search(
+                RoleEntitlementUtil.getRoleKeys(entitlementDAO.findAll()),
+                searchCond, Collections.<OrderByClause>emptyList(), type);
+        for (Subject<?, ?, ?> subject : subjects) {
+            result.add(subject.getKey());
+        }
+
+        return result;
+    }
+
+    public List<Long> findByCorrelationRule(
+            final ConnectorObject connObj, final SyncCorrelationRule rule, final SubjectType type) {
+
+        return search(rule.getSearchCond(connObj), type);
+    }
+
+    public List<Long> findByAttributableSearch(
+            final ConnectorObject connObj,
+            final List<String> altSearchSchemas,
+            final ExternalResource resource,
+            final AttributableUtil attrUtil) {
+
+        // search for external attribute's name/value of each specified name
+        final Map<String, Attribute> extValues = new HashMap<>();
+
+        for (MappingItem item : attrUtil.getMappingItems(resource, MappingPurpose.SYNCHRONIZATION)) {
+            extValues.put(item.getIntAttrName(), connObj.getAttributeByName(item.getExtAttrName()));
+        }
+
+        // search for user/role by attribute(s) specified in the policy
+        SearchCond searchCond = null;
+
+        for (String schema : altSearchSchemas) {
+            Attribute value = extValues.get(schema);
+
+            if (value == null) {
+                throw new IllegalArgumentException(
+                        "Connector object does not contains the attributes to perform the search: " + schema);
+            }
+
+            AttributeCond.Type type;
+            String expression = null;
+
+            if (value.getValue() == null || value.getValue().isEmpty()
+                    || (value.getValue().size() == 1 && value.getValue().get(0) == null)) {
+
+                type = AttributeCond.Type.ISNULL;
+            } else {
+                type = AttributeCond.Type.EQ;
+                expression = value.getValue().size() > 1
+                        ? value.getValue().toString()
+                        : value.getValue().get(0).toString();
+            }
+
+            SearchCond nodeCond;
+            // users: just id or username can be selected to be used
+            // roles: just id or name can be selected to be used
+            if ("id".equalsIgnoreCase(schema)
+                    || "username".equalsIgnoreCase(schema) || "name".equalsIgnoreCase(schema)) {
+
+                SubjectCond cond = new SubjectCond();
+                cond.setSchema(schema);
+                cond.setType(type);
+                cond.setExpression(expression);
+
+                nodeCond = SearchCond.getLeafCond(cond);
+            } else {
+                AttributeCond cond = new AttributeCond();
+                cond.setSchema(schema);
+                cond.setType(type);
+                cond.setExpression(expression);
+
+                nodeCond = SearchCond.getLeafCond(cond);
+            }
+
+            searchCond = searchCond == null
+                    ? nodeCond
+                    : SearchCond.getAndCond(searchCond, nodeCond);
+        }
+
+        return search(searchCond, SubjectType.valueOf(attrUtil.getType().name()));
+    }
+
+    private SyncCorrelationRule getCorrelationRule(final AttributableType type, final SyncPolicySpec policySpec) {
+        String clazz;
+
+        switch (type) {
+            case USER:
+                clazz = policySpec.getUserJavaRule();
+                break;
+            case ROLE:
+                clazz = policySpec.getRoleJavaRule();
+                break;
+            case MEMBERSHIP:
+            case CONFIGURATION:
+            default:
+                clazz = null;
+        }
+
+        SyncCorrelationRule res = null;
+
+        if (StringUtils.isNotBlank(clazz)) {
+            try {
+                res = (SyncCorrelationRule) Class.forName(clazz).newInstance();
+            } catch (Exception e) {
+                LOG.error("Failure instantiating correlation rule class '{}'", clazz, e);
+            }
+        }
+
+        return res;
+    }
+
+    private List<String> getAltSearchSchemas(final AttributableType type, final SyncPolicySpec policySpec) {
+        List<String> result = Collections.emptyList();
+
+        switch (type) {
+            case USER:
+                result = policySpec.getuAltSearchSchemas();
+                break;
+            case ROLE:
+                result = policySpec.getrAltSearchSchemas();
+                break;
+            case MEMBERSHIP:
+            case CONFIGURATION:
+            default:
+        }
+
+        return result;
+    }
+
+    /**
+     * Find users / roles based on mapped uid value (or previous uid value, if updated).
+     *
+     * @param uid for finding by account id
+     * @param connObj for finding by attribute value
+     * @param resource external resource
+     * @param attrUtil attributable util
+     * @return list of matching users / roles
+     */
+    public List<Long> findExisting(
+            final String uid,
+            final ConnectorObject connObj,
+            final ExternalResource resource,
+            final AttributableUtil attrUtil) {
+
+        SyncPolicySpec syncPolicySpec = null;
+        if (resource.getSyncPolicy() == null) {
+            SyncPolicy globalSP = policyDAO.getGlobalSyncPolicy();
+            if (globalSP != null) {
+                syncPolicySpec = globalSP.getSpecification(SyncPolicySpec.class);
+            }
+        } else {
+            syncPolicySpec = resource.getSyncPolicy().getSpecification(SyncPolicySpec.class);
+        }
+
+        SyncCorrelationRule syncRule = null;
+        List<String> altSearchSchemas = null;
+
+        if (syncPolicySpec != null) {
+            syncRule = getCorrelationRule(attrUtil.getType(), syncPolicySpec);
+            altSearchSchemas = getAltSearchSchemas(attrUtil.getType(), syncPolicySpec);
+        }
+
+        return syncRule == null ? altSearchSchemas == null || altSearchSchemas.isEmpty()
+                ? findByAccountIdItem(uid, resource, attrUtil)
+                : findByAttributableSearch(connObj, altSearchSchemas, resource, attrUtil)
+                : findByCorrelationRule(connObj, syncRule, SubjectType.valueOf(attrUtil.getType().name()));
+    }
+
+    public Boolean readEnabled(final ConnectorObject connectorObject, final ProvisioningTask task) {
+        Boolean enabled = null;
+        if (task.isSyncStatus()) {
+            Attribute status = AttributeUtil.find(OperationalAttributes.ENABLE_NAME, connectorObject.getAttributes());
+            if (status != null && status.getValue() != null && !status.getValue().isEmpty()) {
+                enabled = (Boolean) status.getValue().get(0);
+            }
+        }
+
+        return enabled;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserPushResultHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserPushResultHandler.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserPushResultHandler.java
new file mode 100644
index 0000000..3302388
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserPushResultHandler.java
@@ -0,0 +1,159 @@
+/*
+ * 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.server.provisioning.java.sync;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.apache.syncope.common.lib.mod.UserMod;
+import org.apache.syncope.common.lib.to.AbstractSubjectTO;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.PropagationByResource;
+import org.apache.syncope.common.lib.types.ResourceOperation;
+import org.apache.syncope.server.persistence.api.entity.Mapping;
+import org.apache.syncope.server.persistence.api.entity.MappingItem;
+import org.apache.syncope.server.persistence.api.entity.Subject;
+import org.apache.syncope.server.persistence.api.entity.user.User;
+import org.apache.syncope.server.provisioning.api.TimeoutException;
+import org.identityconnectors.framework.common.objects.ConnectorObject;
+import org.identityconnectors.framework.common.objects.ObjectClass;
+import org.identityconnectors.framework.common.objects.Uid;
+
+public class UserPushResultHandler extends AbstractSubjectPushResultHandler {
+
+    @Override
+    protected Subject<?, ?, ?> deprovision(final Subject<?, ?, ?> sbj) {
+        final UserTO before = userTransfer.getUserTO(userDAO.authFetch(sbj.getKey()));
+
+        final List<String> noPropResources = new ArrayList<>(before.getResources());
+        noPropResources.remove(profile.getTask().getResource().getKey());
+
+        taskExecutor.execute(propagationManager.getUserDeleteTaskIds(before.getKey(),
+                Collections.singleton(profile.getTask().getResource().getKey()), noPropResources));
+
+        return userDAO.authFetch(before.getKey());
+    }
+
+    @Override
+    protected Subject<?, ?, ?> provision(final Subject<?, ?, ?> sbj, final Boolean enabled) {
+        final UserTO before = userTransfer.getUserTO(userDAO.authFetch(sbj.getKey()));
+
+        final List<String> noPropResources = new ArrayList<>(before.getResources());
+        noPropResources.remove(profile.getTask().getResource().getKey());
+
+        final PropagationByResource propByRes = new PropagationByResource();
+        propByRes.add(ResourceOperation.CREATE, profile.getTask().getResource().getKey());
+
+        taskExecutor.execute(propagationManager.getUserCreateTaskIds(
+                before.getKey(),
+                enabled,
+                propByRes,
+                null,
+                Collections.unmodifiableCollection(before.getVirAttrs()),
+                Collections.unmodifiableCollection(before.getMemberships()),
+                noPropResources));
+
+        return userDAO.authFetch(before.getKey());
+    }
+
+    @Override
+    protected Subject<?, ?, ?> link(final Subject<?, ?, ?> sbj, final Boolean unlink) {
+        final UserMod userMod = new UserMod();
+        userMod.setKey(sbj.getKey());
+
+        if (unlink) {
+            userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
+        } else {
+            userMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
+        }
+
+        uwfAdapter.update(userMod);
+
+        return userDAO.authFetch(userMod.getKey());
+    }
+
+    @Override
+    protected Subject<?, ?, ?> unassign(final Subject<?, ?, ?> sbj) {
+        final UserMod userMod = new UserMod();
+        userMod.setKey(sbj.getKey());
+        userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
+        uwfAdapter.update(userMod);
+        return deprovision(sbj);
+    }
+
+    @Override
+    protected Subject<?, ?, ?> assign(final Subject<?, ?, ?> sbj, final Boolean enabled) {
+        final UserMod userMod = new UserMod();
+        userMod.setKey(sbj.getKey());
+        userMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
+        uwfAdapter.update(userMod);
+        return provision(sbj, enabled);
+    }
+
+    @Override
+    protected String getName(final Subject<?, ?, ?> subject) {
+        return User.class.cast(subject).getUsername();
+    }
+
+    @Override
+    protected AbstractSubjectTO getSubjectTO(final long key) {
+        try {
+            return userTransfer.getUserTO(userDAO.authFetch(key));
+        } catch (Exception e) {
+            LOG.warn("Error retrieving user {}", key, e);
+            return null;
+        }
+    }
+
+    @Override
+    protected Subject<?, ?, ?> getSubject(final long key) {
+        try {
+            return userDAO.authFetch(key);
+        } catch (Exception e) {
+            LOG.warn("Error retrieving user {}", key, e);
+            return null;
+        }
+    }
+
+    @Override
+    protected ConnectorObject getRemoteObject(final String accountId) {
+        ConnectorObject obj = null;
+
+        try {
+            final Uid uid = new Uid(accountId);
+
+            obj = profile.getConnector().getObject(
+                    ObjectClass.ACCOUNT,
+                    uid,
+                    profile.getConnector().getOperationOptions(Collections.<MappingItem>emptySet()));
+
+        } catch (TimeoutException toe) {
+            LOG.debug("Request timeout", toe);
+            throw toe;
+        } catch (RuntimeException ignore) {
+            LOG.debug("While resolving {}", accountId, ignore);
+        }
+        return obj;
+    }
+
+    @Override
+    protected Mapping<?> getMapping() {
+        return profile.getTask().getResource().getUmapping();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserSyncResultHandler.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserSyncResultHandler.java b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserSyncResultHandler.java
new file mode 100644
index 0000000..58e81ed
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/java/org/apache/syncope/server/provisioning/java/sync/UserSyncResultHandler.java
@@ -0,0 +1,155 @@
+/*
+ * 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.server.provisioning.java.sync;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.apache.syncope.common.lib.mod.AbstractSubjectMod;
+import org.apache.syncope.common.lib.mod.UserMod;
+import org.apache.syncope.common.lib.to.AbstractSubjectTO;
+import org.apache.syncope.common.lib.to.PropagationStatus;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.server.persistence.api.entity.AttributableUtil;
+import org.apache.syncope.server.provisioning.api.sync.ProvisioningResult;
+import org.identityconnectors.framework.common.objects.SyncDelta;
+import org.quartz.JobExecutionException;
+
+public class UserSyncResultHandler extends AbstractSubjectSyncResultHandler {
+
+    @Override
+    protected AttributableUtil getAttributableUtil() {
+        return attrUtilFactory.getInstance(AttributableType.USER);
+    }
+
+    @Override
+    protected String getName(final AbstractSubjectTO subjectTO) {
+        return UserTO.class.cast(subjectTO).getUsername();
+    }
+
+    @Override
+    protected AbstractSubjectTO getSubjectTO(final long key) {
+        try {
+            return userTransfer.getUserTO(userDAO.authFetch(key));
+        } catch (Exception e) {
+            LOG.warn("Error retrieving user {}", key, e);
+            return null;
+        }
+    }
+
+    @Override
+    protected AbstractSubjectMod getSubjectMod(
+            final AbstractSubjectTO subjectTO, final SyncDelta delta) {
+
+        return connObjectUtil.getAttributableMod(
+                subjectTO.getKey(),
+                delta.getObject(),
+                subjectTO,
+                profile.getTask(),
+                getAttributableUtil());
+    }
+
+    @Override
+    protected AbstractSubjectTO create(
+            final AbstractSubjectTO subjectTO, final SyncDelta delta, final ProvisioningResult result) {
+
+        UserTO userTO = UserTO.class.cast(subjectTO);
+
+        Boolean enabled = syncUtilities.readEnabled(delta.getObject(), profile.getTask());
+        //Delegate User Workflow Creation and its Propagation to provisioning manager
+        Map.Entry<Long, List<PropagationStatus>> created = userProvisioningManager.create(userTO, true, true, enabled,
+                Collections.singleton(profile.getTask().getResource().getKey()));
+
+        userTO = userTransfer.getUserTO(userDAO.authFetch(created.getKey()));
+
+        result.setId(created.getKey());
+
+        return userTO;
+    }
+
+    @Override
+    protected List<ProvisioningResult> link(SyncDelta delta, List<Long> subjects, AttributableUtil attrUtil,
+            boolean unlink) throws JobExecutionException {
+        return super.link(delta, subjects, attrUtil, unlink); //To change body of generated methods, choose Tools | Templates.
+    }
+
+    @Override
+    protected AbstractSubjectTO link(
+            final AbstractSubjectTO before,
+            final ProvisioningResult result,
+            final boolean unlink) {
+
+        final UserMod userMod = new UserMod();
+        userMod.setKey(before.getKey());
+
+        if (unlink) {
+            userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
+        } else {
+            userMod.getResourcesToAdd().add(profile.getTask().getResource().getKey());
+        }
+
+        return userTransfer.getUserTO(userDAO.authFetch(uwfAdapter.update(userMod).getResult().getKey().getKey()));
+    }
+
+    @Override
+    protected AbstractSubjectTO update(
+            final AbstractSubjectTO before,
+            final AbstractSubjectMod subjectMod,
+            final SyncDelta delta,
+            final ProvisioningResult result) {
+
+        final UserMod userMod = UserMod.class.cast(subjectMod);
+        final Boolean enabled = syncUtilities.readEnabled(delta.getObject(), profile.getTask());
+
+        Map.Entry<Long, List<PropagationStatus>> updated = userProvisioningManager.update(userMod, before.getKey(),
+                result, enabled, Collections.singleton(profile.getTask().getResource().getKey()));
+
+        return userTransfer.getUserTO(userDAO.authFetch(updated.getKey()));
+    }
+
+    @Override
+    protected void deprovision(
+            final Long key,
+            final boolean unlink) {
+
+        taskExecutor.execute(
+                propagationManager.getUserDeleteTaskIds(key, profile.getTask().getResource().getKey()));
+
+        if (unlink) {
+            final UserMod userMod = new UserMod();
+            userMod.setKey(key);
+            userMod.getResourcesToRemove().add(profile.getTask().getResource().getKey());
+        }
+    }
+
+    @Override
+    protected void delete(final Long key) {
+        try {
+            userProvisioningManager.
+                    delete(key, Collections.<String>singleton(profile.getTask().getResource().getKey()));
+        } catch (Exception e) {
+            // A propagation failure doesn't imply a synchronization failure.
+            // The propagation exception status will be reported into the propagation task execution.
+            LOG.error("Could not propagate user " + key, e);
+        }
+
+        uwfAdapter.delete(key);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/main/resources/connid.properties
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/resources/connid.properties b/syncope620/server/provisioning-java/src/main/resources/connid.properties
new file mode 100644
index 0000000..24d5c93
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/resources/connid.properties
@@ -0,0 +1,17 @@
+# 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.
+connid.locations=${connid.location}

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/main/resources/mail.properties
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/resources/mail.properties b/syncope620/server/provisioning-java/src/main/resources/mail.properties
new file mode 100644
index 0000000..17bda2e
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/resources/mail.properties
@@ -0,0 +1,24 @@
+# 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.
+smtpHost=none.syncope.apache.org
+smtpPort=25
+smtpUser=
+smtpPassword=
+smtpProtocol=smtp
+smtpEncoding=UTF-8
+smtpConnectionTimeout=3000
+mailDebug=false
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.html.vm
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.html.vm b/syncope620/server/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.html.vm
new file mode 100644
index 0000000..90630ac
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.html.vm
@@ -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.
+-->
+<html>
+<body>
+<p>Hi,</br>
+we are happy to inform you that the password request was execute successfully for your account.</p>
+
+<p>Best regards.</p>
+</body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.txt.vm
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.txt.vm b/syncope620/server/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.txt.vm
new file mode 100644
index 0000000..33f75dc
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/resources/mailTemplates/confirmPasswordReset.txt.vm
@@ -0,0 +1,20 @@
+# 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.
+Hi,
+we are happy to inform you that the password request was execute successfully for your account.
+
+Best regards.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/main/resources/mailTemplates/optin.html.vm
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/resources/mailTemplates/optin.html.vm b/syncope620/server/provisioning-java/src/main/resources/mailTemplates/optin.html.vm
new file mode 100644
index 0000000..007b09b
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/resources/mailTemplates/optin.html.vm
@@ -0,0 +1,72 @@
+<!--
+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.
+-->
+<html>
+<body>
+<h3>Hi $user.getAttrMap().get("firstname").getValues().get(0) $user.getAttrMap().get("surname").getValues().get(0), welcome to Syncope!</h3>
+
+<p>
+   Your username is $user.getUsername().<br/>
+   Your email address is $user.getAttrMap().get("email").getValues().get(0).
+   Your email address inside a <a href="http://localhost/?email=$esc.url($user.getAttrMap().get("email").getValues().get(0))">link</a>.
+</p>
+
+<p>
+    This message was sent to the following recipients:
+<ul>
+#foreach($recipient in $recipients)
+  <li>$recipient.getAttrMap().get("email").getValues().get(0)</li>
+#end
+</ul>
+
+because one of the following events occurred:
+<ul>
+#foreach($event in $events)
+  <li>$event</i>
+#end
+</ul>
+</p>
+
+#if(!$user.getMemberships().isEmpty())
+You have been provided with the following roles:
+<ul>
+#foreach($membership in $user.getMemberships())
+  <li>$membership.roleName</i>
+#end
+</ul>
+#end
+
+#if(${output.class.simpleName} == "TaskExec")
+Below you can read execution details of task $output.getTask().getClass().getSimpleName(), id $output.getId().
+Task Details:
+<ul>
+<li>
+START DATE:&nbsp$output.getStartDate()
+</li>
+<li>
+MESSAGE:<br/>
+$output.getMessage()
+</li>
+<li>
+END DATE:&nbsp$output.getEndDate()
+</li>
+</ul>
+#end
+
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/main/resources/mailTemplates/optin.txt.vm
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/resources/mailTemplates/optin.txt.vm b/syncope620/server/provisioning-java/src/main/resources/mailTemplates/optin.txt.vm
new file mode 100644
index 0000000..b79b317
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/resources/mailTemplates/optin.txt.vm
@@ -0,0 +1,51 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+Hi $user.getAttrMap().get("firstname").getValues().get(0) $user.getAttrMap().get("surname").getValues().get(0), welcome to Syncope!
+
+Your username is $user.getUsername().
+Your email address is $user.getAttrMap().get("email").getValues().get(0).
+Your email address inside a link: http://localhost/?email=$esc.url($user.getAttrMap().get("email").getValues().get(0)) .
+
+This message was sent to the following recipients:
+#foreach($recipient in $recipients)
+   * $recipient.getAttrMap().get("surname").getValues().get(0)
+#end
+
+because one of the following events occurred:
+#foreach($event in $events)
+  * $event
+#end
+
+#if(!$user.getMemberships().isEmpty())
+You have been provided with the following roles:
+#foreach($membership in $user.getMemberships())
+  * $membership.roleName
+#end
+#end
+
+#if(${output.class.simpleName} == "TaskExec")
+Below you can read execution details of task $output.getTask().getClass().getSimpleName(), id $output.getId()
+
+Task Details:
+
+  * START DATE: $output.getStartDate() 
+
+  * MESSAGE:
+$output.getMessage()
+
+  * END DATE: $output.getEndDate()
+#end

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.html.vm
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.html.vm b/syncope620/server/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.html.vm
new file mode 100644
index 0000000..6594c3f
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.html.vm
@@ -0,0 +1,31 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<html>
+<body>
+<p>Hi,
+a password reset was request for $user.getUsername().</p>
+
+<p>In order to complete this request, you need to visit this 
+<a href="http://localhost:9080/syncope-console/?pwdResetToken=$input.get(0)">link</a></p>.
+
+<p>If you did not request this reset, just ignore the present e-mail.</p>
+
+<p>Best regards.</p>
+</body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.txt.vm
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.txt.vm b/syncope620/server/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.txt.vm
new file mode 100644
index 0000000..5ac028a
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/resources/mailTemplates/requestPasswordReset.txt.vm
@@ -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.
+Hi,
+a password reset was request for $user.getUsername().
+
+In order to complete this request, you need to visit this link:
+
+http://localhost:9080/syncope-console/?pwdResetToken=$input.get(0)
+
+If you did not request this reset, just ignore the present e-mail.
+
+Best regards.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/main/resources/provisioningContext.xml
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/main/resources/provisioningContext.xml b/syncope620/server/provisioning-java/src/main/resources/provisioningContext.xml
new file mode 100644
index 0000000..a93ba4e
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/main/resources/provisioningContext.xml
@@ -0,0 +1,101 @@
+<?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"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+                           http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/context
+                           http://www.springframework.org/schema/context/spring-context.xsd">
+  
+  <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"
+        lazy-init="false" depends-on="nonJPAdbInitializer">
+    <property name="autoStartup" value="true"/>
+    <property name="applicationContextSchedulerContextKey" value="applicationContext"/>
+    <property name="waitForJobsToCompleteOnShutdown" value="true"/>
+    <property name="overwriteExistingJobs" value="true"/>
+    <property name="dataSource" ref="dataSource"/>
+    <property name="transactionManager" ref="transactionManager"/>
+    <property name="jobFactory">
+      <bean class="org.springframework.scheduling.quartz.SpringBeanJobFactory"/>
+    </property>
+    <property name="quartzProperties">
+      <props>
+        <prop key="org.quartz.scheduler.idleWaitTime">${quartz.scheduler.idleWaitTime:30000}</prop>
+
+        <prop key="org.quartz.jobStore.misfireThreshold">6000000</prop>
+        <prop key="org.quartz.jobStore.driverDelegateClass">${quartz.jobstore}</prop>
+
+        <prop key="org.quartz.jobStore.isClustered">true</prop>
+        <prop key="org.quartz.jobStore.clusterCheckinInterval">20000</prop>
+
+        <prop key="org.quartz.scheduler.instanceName">ClusteredScheduler</prop>
+        <prop key="org.quartz.scheduler.instanceId">AUTO</prop>
+        <prop key="org.quartz.scheduler.jmx.export">true</prop>
+      </props>
+    </property>
+  </bean>
+  
+  <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
+    <property name="defaultEncoding" value="${smtpEncoding}"/>
+    <property name="host" value="${smtpHost}"/>
+    <property name="port" value="${smtpPort}"/>
+    <property name="username" value="${smtpUser}"/>
+    <property name="password" value="${smtpPassword}"/>
+    <property name="protocol" value="${smtpProtocol}"/>
+
+    <property name="javaMailProperties">
+      <props>
+        <prop key="mail.smtp.connectiontimeout">${smtpConnectionTimeout}</prop>
+        <prop key="mail.debug">${mailDebug}</prop>
+      </props>
+    </property>
+  </bean>
+
+  <bean class="org.apache.syncope.server.provisioning.java.propagation.PropagationManagerImpl"/>
+  <bean class="org.apache.syncope.server.provisioning.java.propagation.DefaultPropagationReporter" scope="prototype"/>
+
+  <context:component-scan base-package="org.apache.syncope.server.misc"/>  
+  <context:component-scan base-package="org.apache.syncope.server.provisioning.java"/>
+
+  <bean id="virAttrCache" class="org.apache.syncope.server.provisioning.java.cache.MemoryVirAttrCache" scope="singleton">
+    <constructor-arg value="60"/>
+    <constructor-arg value="5000"/>
+  </bean>
+
+  <bean id="velocityResourceLoader" class="org.apache.syncope.server.misc.spring.ResourceWithFallbackLoader">
+    <property name="primary" value="file:${conf.directory}/"/>
+    <property name="fallback" value="classpath:"/>
+  </bean>
+  <bean id="velocityEngine" class="org.apache.syncope.server.provisioning.java.notification.VelocityEngineFactoryBean">
+    <property name="resourceLoader" ref="velocityResourceLoader"/>
+  </bean>
+  <bean id="velocityToolManager" class="org.apache.velocity.tools.ToolManager">
+    <!-- autoConfigure -->
+    <constructor-arg index="0" value="true"/>
+    <!-- include default velocity tools -->
+    <constructor-arg index="1" value="true"/>
+  </bean>
+
+  <bean id="connIdBundleManager" class="org.apache.syncope.server.provisioning.java.ConnIdBundleManagerImpl" scope="singleton">
+    <property name="stringLocations" value="${connid.locations}"/>
+  </bean>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/test/java/org/apache/syncope/server/provisioning/java/AbstractTest.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/test/java/org/apache/syncope/server/provisioning/java/AbstractTest.java b/syncope620/server/provisioning-java/src/test/java/org/apache/syncope/server/provisioning/java/AbstractTest.java
new file mode 100644
index 0000000..62b0d6c
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/test/java/org/apache/syncope/server/provisioning/java/AbstractTest.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.server.provisioning.java;
+
+import org.junit.runner.RunWith;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(locations = {
+    "classpath:persistenceTest.xml",
+    "classpath:provisioningContext.xml",
+    "classpath:provisioningTest.xml"
+})
+public abstract class AbstractTest {
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/test/java/org/apache/syncope/server/provisioning/java/ConnectorManagerTest.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/test/java/org/apache/syncope/server/provisioning/java/ConnectorManagerTest.java b/syncope620/server/provisioning-java/src/test/java/org/apache/syncope/server/provisioning/java/ConnectorManagerTest.java
new file mode 100644
index 0000000..3ad5206
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/test/java/org/apache/syncope/server/provisioning/java/ConnectorManagerTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.server.provisioning.java;
+
+import org.apache.syncope.server.provisioning.java.ConnectorManager;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.syncope.server.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.server.persistence.api.entity.ExternalResource;
+import org.apache.syncope.server.provisioning.api.ConnIdBundleManager;
+import org.apache.syncope.server.provisioning.api.Connector;
+import org.apache.syncope.server.misc.spring.ApplicationContextProvider;
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.util.ReflectionTestUtils;
+import org.springframework.transaction.annotation.Transactional;
+
+@Transactional
+public class ConnectorManagerTest extends AbstractTest {
+
+    private ConnectorManager connManager;
+
+    @Autowired
+    private ConnIdBundleManager connIdBundleManager;
+
+    @Autowired
+    private ExternalResourceDAO resourceDAO;
+
+    @Before
+    public void before() {
+        connManager = new ConnectorManager();
+        ReflectionTestUtils.setField(connManager, "connIdBundleManager", connIdBundleManager);
+        ReflectionTestUtils.setField(connManager, "resourceDAO", resourceDAO);
+
+        // Remove any other connector instance bean set up by standard ConnectorManager.load()
+        connManager.unload();
+    }
+
+    @Test
+    public void load() {
+        connManager.load();
+
+        // only consider local connector bundles
+        int expected = 0;
+        for (ExternalResource resource : resourceDAO.findAll()) {
+            if (resource.getConnector().getLocation().startsWith("file")) {
+                expected++;
+            }
+        }
+
+        assertEquals(expected,
+                ApplicationContextProvider.getApplicationContext().
+                getBeanNamesForType(Connector.class, false, true).length);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/test/java/org/apache/syncope/server/provisioning/java/data/ResourceDataBinderTest.java
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/test/java/org/apache/syncope/server/provisioning/java/data/ResourceDataBinderTest.java b/syncope620/server/provisioning-java/src/test/java/org/apache/syncope/server/provisioning/java/data/ResourceDataBinderTest.java
new file mode 100644
index 0000000..8b8a39b
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/test/java/org/apache/syncope/server/provisioning/java/data/ResourceDataBinderTest.java
@@ -0,0 +1,152 @@
+/*
+ * 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.server.provisioning.java.data;
+
+import org.apache.syncope.server.provisioning.java.AbstractTest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.apache.syncope.common.lib.to.MappingItemTO;
+import org.apache.syncope.common.lib.to.MappingTO;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.common.lib.types.IntMappingType;
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.syncope.common.lib.types.PropagationMode;
+import org.apache.syncope.server.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.server.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.server.persistence.api.entity.ExternalResource;
+import org.apache.syncope.server.persistence.api.entity.MappingItem;
+import org.apache.syncope.server.persistence.api.entity.user.UPlainSchema;
+import org.apache.syncope.server.provisioning.api.data.ResourceDataBinder;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+
+@Transactional
+public class ResourceDataBinderTest extends AbstractTest {
+
+    @Autowired
+    private ExternalResourceDAO resourceDAO;
+
+    @Autowired
+    private ResourceDataBinder resourceDataBinder;
+
+    @Autowired
+    private PlainSchemaDAO plainSchemaDAO;
+
+    @Test
+    public void databinding() throws IOException {
+        ExternalResource resource = resourceDAO.find("ws-target-resource-2");
+        assertNotNull(resource);
+
+        ResourceTO resourceTO = resourceDataBinder.getResourceTO(resource);
+        assertNotNull(resourceTO);
+
+        ExternalResource fromto = resourceDataBinder.update(resource, resourceTO);
+        assertNotNull(fromto);
+        assertEquals(resource, fromto);
+
+        ObjectMapper mapper = new ObjectMapper();
+
+        StringWriter writer = new StringWriter();
+        mapper.writeValue(writer, resourceTO);
+
+        assertEquals(resourceTO, mapper.readValue(writer.toString(), ResourceTO.class));
+
+        List<ResourceTO> resourceTOs = resourceDataBinder.getResourceTOs(resourceDAO.findAll());
+        assertNotNull(resourceTOs);
+        assertFalse(resourceTOs.isEmpty());
+
+        writer = new StringWriter();
+        mapper.writeValue(writer, resourceTOs);
+
+        ResourceTO[] actual = mapper.readValue(writer.toString(), ResourceTO[].class);
+        assertEquals(resourceTOs, Arrays.asList(actual));
+    }
+
+    @Test
+    public void issue42() {
+        UPlainSchema userId = plainSchemaDAO.find("userId", UPlainSchema.class);
+
+        Set<MappingItem> beforeUserIdMappings = new HashSet<>();
+        for (ExternalResource res : resourceDAO.findAll()) {
+            if (res.getUmapping() != null) {
+                for (MappingItem mapItem : res.getUmapping().getItems()) {
+                    if (userId.getKey().equals(mapItem.getIntAttrName())) {
+                        beforeUserIdMappings.add(mapItem);
+                    }
+                }
+            }
+        }
+
+        ResourceTO resourceTO = new ResourceTO();
+        resourceTO.setKey("resource-issue42");
+        resourceTO.setConnectorId(100L);
+        resourceTO.setPropagationMode(PropagationMode.ONE_PHASE);
+        resourceTO.setEnforceMandatoryCondition(true);
+
+        MappingTO mapping = new MappingTO();
+        resourceTO.setUmapping(mapping);
+
+        MappingItemTO item = new MappingItemTO();
+        item.setIntAttrName("userId");
+        item.setIntMappingType(IntMappingType.UserSchema);
+        item.setExtAttrName("campo1");
+        item.setAccountid(true);
+        item.setMandatoryCondition("false");
+        item.setPurpose(MappingPurpose.BOTH);
+        mapping.setAccountIdItem(item);
+
+        ExternalResource resource = resourceDataBinder.create(resourceTO);
+        resource = resourceDAO.save(resource);
+        assertNotNull(resource);
+        assertNotNull(resource.getUmapping());
+        assertEquals(1, resource.getUmapping().getItems().size());
+
+        resourceDAO.flush();
+
+        ExternalResource actual = resourceDAO.find("resource-issue42");
+        assertNotNull(actual);
+        assertEquals(resource, actual);
+
+        userId = plainSchemaDAO.find("userId", UPlainSchema.class);
+
+        Set<MappingItem> afterUserIdMappings = new HashSet<>();
+        for (ExternalResource res : resourceDAO.findAll()) {
+            if (res.getUmapping() != null) {
+                for (MappingItem mapItem : res.getUmapping().getItems()) {
+                    if (userId.getKey().equals(mapItem.getIntAttrName())) {
+                        afterUserIdMappings.add(mapItem);
+                    }
+                }
+            }
+        }
+
+        assertEquals(beforeUserIdMappings.size(), afterUserIdMappings.size() - 1);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/provisioning-java/src/test/resources/provisioningTest.xml
----------------------------------------------------------------------
diff --git a/syncope620/server/provisioning-java/src/test/resources/provisioningTest.xml b/syncope620/server/provisioning-java/src/test/resources/provisioningTest.xml
new file mode 100644
index 0000000..e739b8c
--- /dev/null
+++ b/syncope620/server/provisioning-java/src/test/resources/provisioningTest.xml
@@ -0,0 +1,47 @@
+<?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">
+    
+  <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+    <property name="locations">
+      <list>
+        <value>classpath:persistence.properties</value>
+        <value>classpath:security.properties</value>
+        <value>classpath:connid.properties</value>
+        <value>classpath:mail.properties</value>
+      </list>
+    </property>
+    <property name="ignoreResourceNotFound" value="true"/>
+    <property name="ignoreUnresolvablePlaceholders" value="true"/>
+  </bean>
+  
+  <bean id="contentXML" class="org.apache.syncope.server.misc.spring.ResourceWithFallbackLoader">
+    <property name="primary" value="file:${conf.directory}/content.xml"/>
+    <property name="fallback" value="classpath:content.xml"/>
+  </bean>
+  <bean class="org.apache.syncope.server.persistence.jpa.content.XMLContentLoader" init-method="load"/>
+  
+  <bean class="org.apache.syncope.server.workflow.java.DefaultUserWorkflowAdapter"/>
+  <bean class="org.apache.syncope.server.workflow.java.DefaultRoleWorkflowAdapter"/>
+  
+</beans>

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/security/pom.xml
----------------------------------------------------------------------
diff --git a/syncope620/server/security/pom.xml b/syncope620/server/security/pom.xml
deleted file mode 100644
index 255f04d..0000000
--- a/syncope620/server/security/pom.xml
+++ /dev/null
@@ -1,76 +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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-
-  <modelVersion>4.0.0</modelVersion>
-
-  <parent>
-    <groupId>org.apache.syncope</groupId>
-    <artifactId>syncope-server</artifactId>
-    <version>2.0.0-SNAPSHOT</version>
-  </parent>
-
-  <name>Apache Syncope Server Security Features</name>
-  <description>Apache Syncope Server Security Features</description>
-  <groupId>org.apache.syncope.server</groupId>
-  <artifactId>syncope-server-security</artifactId>
-  <packaging>jar</packaging>
-
-  <dependencies>
-    <dependency>
-      <groupId>commons-io</groupId>
-      <artifactId>commons-io</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.commons</groupId>
-      <artifactId>commons-lang3</artifactId>
-    </dependency>
-    
-    <dependency>
-      <groupId>org.jasypt</groupId>
-      <artifactId>jasypt</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework.security</groupId>
-      <artifactId>spring-security-core</artifactId>
-    </dependency>
-      
-    <dependency>
-      <groupId>org.slf4j</groupId>
-      <artifactId>slf4j-api</artifactId>
-    </dependency>
-    
-    <dependency>
-      <groupId>org.apache.syncope.common</groupId>
-      <artifactId>syncope-common-lib</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-  </dependencies>
-
-  <build>
-    <resources>
-      <resource>
-        <directory>src/main/resources</directory>
-        <filtering>true</filtering>
-      </resource>
-    </resources>
-  </build>
-
-</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/235f60fa/syncope620/server/security/src/main/java/org/apache/syncope/server/security/AuthContextUtil.java
----------------------------------------------------------------------
diff --git a/syncope620/server/security/src/main/java/org/apache/syncope/server/security/AuthContextUtil.java b/syncope620/server/security/src/main/java/org/apache/syncope/server/security/AuthContextUtil.java
deleted file mode 100644
index f414a37..0000000
--- a/syncope620/server/security/src/main/java/org/apache/syncope/server/security/AuthContextUtil.java
+++ /dev/null
@@ -1,55 +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.server.security;
-
-import java.util.HashSet;
-import java.util.Set;
-import org.apache.syncope.common.lib.SyncopeConstants;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.context.SecurityContext;
-import org.springframework.security.core.context.SecurityContextHolder;
-
-public final class AuthContextUtil {
-
-    public static String getAuthenticatedUsername() {
-        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
-        return authentication == null ? SyncopeConstants.UNAUTHENTICATED : authentication.getName();
-    }
-
-    public static Set<String> getOwnedEntitlementNames() {
-        final Set<String> result = new HashSet<String>();
-
-        final SecurityContext ctx = SecurityContextHolder.getContext();
-
-        if (ctx != null && ctx.getAuthentication() != null && ctx.getAuthentication().getAuthorities() != null) {
-            for (GrantedAuthority authority : ctx.getAuthentication().getAuthorities()) {
-                result.add(authority.getAuthority());
-            }
-        }
-
-        return result;
-    }
-
-    /**
-     * Private default constructor, for static-only classes.
-     */
-    private AuthContextUtil() {
-    }
-}