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/02/12 10:14:54 UTC

[49/54] [abbrv] [partial] syncope git commit: [SYNCOPE-620] Renaming 'server' after 'core', to provide continuity with older releases (especially for archetype)

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/NotificationLogic.java
----------------------------------------------------------------------
diff --git a/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/NotificationLogic.java b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/NotificationLogic.java
new file mode 100644
index 0000000..0c7a8e2
--- /dev/null
+++ b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/NotificationLogic.java
@@ -0,0 +1,127 @@
+/*
+ * 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.logic;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.syncope.common.lib.to.NotificationTO;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.NotificationDAO;
+import org.apache.syncope.core.persistence.api.entity.Notification;
+import org.apache.syncope.core.provisioning.api.data.NotificationDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+@Component
+public class NotificationLogic extends AbstractTransactionalLogic<NotificationTO> {
+
+    @Autowired
+    private NotificationDAO notificationDAO;
+
+    @Autowired
+    private NotificationDataBinder binder;
+
+    @PreAuthorize("hasRole('NOTIFICATION_READ')")
+    public NotificationTO read(final Long notificationId) {
+        Notification notification = notificationDAO.find(notificationId);
+        if (notification == null) {
+            LOG.error("Could not find notification '" + notificationId + "'");
+
+            throw new NotFoundException(String.valueOf(notificationId));
+        }
+
+        return binder.getNotificationTO(notification);
+    }
+
+    @PreAuthorize("hasRole('NOTIFICATION_LIST')")
+    public List<NotificationTO> list() {
+        List<Notification> notifications = notificationDAO.findAll();
+
+        List<NotificationTO> notificationTOs = new ArrayList<NotificationTO>();
+        for (Notification notification : notifications) {
+            notificationTOs.add(binder.getNotificationTO(notification));
+        }
+
+        return notificationTOs;
+    }
+
+    @PreAuthorize("hasRole('NOTIFICATION_CREATE')")
+    public NotificationTO create(final NotificationTO notificationTO) {
+        return binder.getNotificationTO(notificationDAO.save(binder.create(notificationTO)));
+    }
+
+    @PreAuthorize("hasRole('NOTIFICATION_UPDATE')")
+    public NotificationTO update(final NotificationTO notificationTO) {
+        Notification notification = notificationDAO.find(notificationTO.getKey());
+        if (notification == null) {
+            LOG.error("Could not find notification '" + notificationTO.getKey() + "'");
+            throw new NotFoundException(String.valueOf(notificationTO.getKey()));
+        }
+
+        binder.update(notification, notificationTO);
+        notification = notificationDAO.save(notification);
+
+        return binder.getNotificationTO(notification);
+    }
+
+    @PreAuthorize("hasRole('CONNECTOR_DELETE')")
+    public NotificationTO delete(final Long notificationId) {
+        Notification notification = notificationDAO.find(notificationId);
+        if (notification == null) {
+            LOG.error("Could not find notification '" + notificationId + "'");
+
+            throw new NotFoundException(String.valueOf(notificationId));
+        }
+
+        NotificationTO deleted = binder.getNotificationTO(notification);
+        notificationDAO.delete(notificationId);
+        return deleted;
+    }
+
+    @Override
+    protected NotificationTO resolveReference(final Method method, final Object... args)
+            throws UnresolvedReferenceException {
+
+        Long key = null;
+
+        if (ArrayUtils.isNotEmpty(args)) {
+            for (int i = 0; key == null && i < args.length; i++) {
+                if (args[i] instanceof Long) {
+                    key = (Long) args[i];
+                } else if (args[i] instanceof NotificationTO) {
+                    key = ((NotificationTO) args[i]).getKey();
+                }
+            }
+        }
+
+        if ((key != null) && !key.equals(0l)) {
+            try {
+                return binder.getNotificationTO(notificationDAO.find(key));
+            } catch (Throwable ignore) {
+                LOG.debug("Unresolved reference", ignore);
+                throw new UnresolvedReferenceException(ignore);
+            }
+        }
+
+        throw new UnresolvedReferenceException();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/PolicyLogic.java
----------------------------------------------------------------------
diff --git a/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/PolicyLogic.java b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/PolicyLogic.java
new file mode 100644
index 0000000..c01131c
--- /dev/null
+++ b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/PolicyLogic.java
@@ -0,0 +1,187 @@
+/*
+ * 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.logic;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.syncope.common.lib.to.AbstractPolicyTO;
+import org.apache.syncope.common.lib.to.AccountPolicyTO;
+import org.apache.syncope.common.lib.to.PasswordPolicyTO;
+import org.apache.syncope.common.lib.to.SyncPolicyTO;
+import org.apache.syncope.common.lib.types.PolicyType;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
+import org.apache.syncope.core.persistence.api.entity.AccountPolicy;
+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.SyncPolicy;
+import org.apache.syncope.core.provisioning.api.data.PolicyDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+@Component
+public class PolicyLogic extends AbstractTransactionalLogic<AbstractPolicyTO> {
+
+    @Autowired
+    private PolicyDAO policyDAO;
+
+    @Autowired
+    private PolicyDataBinder binder;
+
+    @PreAuthorize("hasRole('POLICY_CREATE')")
+    public <T extends AbstractPolicyTO> T create(final T policyTO) {
+        return binder.getPolicyTO(policyDAO.save(binder.getPolicy(null, policyTO)));
+    }
+
+    private <T extends AbstractPolicyTO, K extends Policy> T update(final T policyTO, final K policy) {
+        binder.getPolicy(policy, policyTO);
+        K savedPolicy = policyDAO.save(policy);
+        return binder.getPolicyTO(savedPolicy);
+    }
+
+    @PreAuthorize("hasRole('POLICY_UPDATE')")
+    public PasswordPolicyTO update(final PasswordPolicyTO policyTO) {
+        Policy policy = policyDAO.find(policyTO.getKey());
+        if (!(policy instanceof PasswordPolicy)) {
+            throw new NotFoundException("PasswordPolicy with id " + policyTO.getKey());
+        }
+
+        return update(policyTO, policy);
+    }
+
+    @PreAuthorize("hasRole('POLICY_UPDATE')")
+    public AccountPolicyTO update(final AccountPolicyTO policyTO) {
+        Policy policy = policyDAO.find(policyTO.getKey());
+        if (!(policy instanceof AccountPolicy)) {
+            throw new NotFoundException("AccountPolicy with id " + policyTO.getKey());
+        }
+
+        return update(policyTO, policy);
+    }
+
+    @PreAuthorize("hasRole('POLICY_UPDATE')")
+    public SyncPolicyTO update(final SyncPolicyTO policyTO) {
+        Policy policy = policyDAO.find(policyTO.getKey());
+        if (!(policy instanceof SyncPolicy)) {
+            throw new NotFoundException("SyncPolicy with id " + policyTO.getKey());
+        }
+
+        return update(policyTO, policy);
+    }
+
+    @PreAuthorize("hasRole('POLICY_LIST')")
+    @SuppressWarnings("unchecked")
+    public <T extends AbstractPolicyTO> List<T> list(final PolicyType type) {
+
+        List<? extends Policy> policies = policyDAO.find(type);
+
+        final List<T> policyTOs = new ArrayList<T>();
+        for (Policy policy : policies) {
+            policyTOs.add((T) binder.getPolicyTO(policy));
+        }
+
+        return policyTOs;
+    }
+
+    @PreAuthorize("hasRole('POLICY_READ')")
+    public PasswordPolicyTO getGlobalPasswordPolicy() {
+        PasswordPolicy policy = policyDAO.getGlobalPasswordPolicy();
+        if (policy == null) {
+            throw new NotFoundException("No password policy found");
+        }
+
+        return (PasswordPolicyTO) binder.getPolicyTO(policy);
+    }
+
+    @PreAuthorize("hasRole('POLICY_READ')")
+    public AccountPolicyTO getGlobalAccountPolicy() {
+        AccountPolicy policy = policyDAO.getGlobalAccountPolicy();
+        if (policy == null) {
+            throw new NotFoundException("No account policy found");
+        }
+
+        return (AccountPolicyTO) binder.getPolicyTO(policy);
+    }
+
+    @PreAuthorize("hasRole('POLICY_READ')")
+    public SyncPolicyTO getGlobalSyncPolicy() {
+        SyncPolicy policy = policyDAO.getGlobalSyncPolicy();
+        if (policy == null) {
+            throw new NotFoundException("No sync policy found");
+        }
+
+        return (SyncPolicyTO) binder.getPolicyTO(policy);
+    }
+
+    @PreAuthorize("hasRole('POLICY_READ')")
+    public <T extends AbstractPolicyTO> T read(final Long id) {
+        Policy policy = policyDAO.find(id);
+        if (policy == null) {
+            throw new NotFoundException("Policy " + id + " not found");
+        }
+
+        return binder.getPolicyTO(policy);
+    }
+
+    @PreAuthorize("hasRole('POLICY_DELETE')")
+    public <T extends AbstractPolicyTO> T delete(final Long id) {
+        Policy policy = policyDAO.find(id);
+        if (policy == null) {
+            throw new NotFoundException("Policy " + id + " not found");
+        }
+
+        T policyToDelete = binder.getPolicyTO(policy);
+        policyDAO.delete(policy);
+
+        return policyToDelete;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected AbstractPolicyTO resolveReference(final Method method, final Object... args)
+            throws UnresolvedReferenceException {
+        Long id = null;
+
+        if (ArrayUtils.isNotEmpty(args)) {
+            for (int i = 0; id == null && i < args.length; i++) {
+                if (args[i] instanceof Long) {
+                    id = (Long) args[i];
+                } else if (args[i] instanceof AbstractPolicyTO) {
+                    id = ((AbstractPolicyTO) args[i]).getKey();
+                }
+            }
+        }
+
+        if ((id != null) && !id.equals(0l)) {
+            try {
+                return binder.getPolicyTO(policyDAO.find(id));
+            } catch (Throwable ignore) {
+                LOG.debug("Unresolved reference", ignore);
+                throw new UnresolvedReferenceException(ignore);
+            }
+        }
+
+        throw new UnresolvedReferenceException();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
----------------------------------------------------------------------
diff --git a/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
new file mode 100644
index 0000000..a634433
--- /dev/null
+++ b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
@@ -0,0 +1,391 @@
+/*
+ * 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.logic;
+
+import java.io.ByteArrayInputStream;
+import java.io.OutputStream;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.zip.ZipInputStream;
+import org.apache.cocoon.optional.pipeline.components.sax.fop.FopSerializer;
+import org.apache.cocoon.pipeline.NonCachingPipeline;
+import org.apache.cocoon.pipeline.Pipeline;
+import org.apache.cocoon.sax.SAXPipelineComponent;
+import org.apache.cocoon.sax.component.XMLGenerator;
+import org.apache.cocoon.sax.component.XMLSerializer;
+import org.apache.cocoon.sax.component.XSLTTransformer;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.common.lib.to.ReportExecTO;
+import org.apache.syncope.common.lib.to.ReportTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.ReportExecExportFormat;
+import org.apache.syncope.common.lib.types.ReportExecStatus;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.ReportDAO;
+import org.apache.syncope.core.persistence.api.dao.ReportExecDAO;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.Report;
+import org.apache.syncope.core.persistence.api.entity.ReportExec;
+import org.apache.syncope.core.provisioning.api.data.ReportDataBinder;
+import org.apache.syncope.core.provisioning.api.job.JobNamer;
+import org.apache.syncope.core.logic.init.ImplementationClassNamesLoader;
+import org.apache.syncope.core.provisioning.api.job.JobInstanceLoader;
+import org.apache.syncope.core.logic.report.Reportlet;
+import org.apache.syncope.core.logic.report.ReportletConfClass;
+import org.apache.syncope.core.logic.report.TextSerializer;
+import org.apache.xmlgraphics.util.MimeConstants;
+import org.quartz.JobKey;
+import org.quartz.Scheduler;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.quartz.SchedulerFactoryBean;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.ClassUtils;
+
+@Component
+public class ReportLogic extends AbstractTransactionalLogic<ReportTO> {
+
+    @Autowired
+    private ReportDAO reportDAO;
+
+    @Autowired
+    private ReportExecDAO reportExecDAO;
+
+    @Autowired
+    private JobInstanceLoader jobInstanceLoader;
+
+    @Autowired
+    private SchedulerFactoryBean scheduler;
+
+    @Autowired
+    private ReportDataBinder binder;
+
+    @Autowired
+    private EntityFactory entityFactory;
+
+    @Autowired
+    private ImplementationClassNamesLoader classNamesLoader;
+
+    @PreAuthorize("hasRole('REPORT_CREATE')")
+    public ReportTO create(final ReportTO reportTO) {
+        Report report = entityFactory.newEntity(Report.class);
+        binder.getReport(report, reportTO);
+        report = reportDAO.save(report);
+
+        try {
+            jobInstanceLoader.registerJob(report);
+        } catch (Exception e) {
+            LOG.error("While registering quartz job for report " + report.getKey(), e);
+
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
+            sce.getElements().add(e.getMessage());
+            throw sce;
+        }
+
+        return binder.getReportTO(report);
+    }
+
+    @PreAuthorize("hasRole('REPORT_UPDATE')")
+    public ReportTO update(final ReportTO reportTO) {
+        Report report = reportDAO.find(reportTO.getKey());
+        if (report == null) {
+            throw new NotFoundException("Report " + reportTO.getKey());
+        }
+
+        binder.getReport(report, reportTO);
+        report = reportDAO.save(report);
+
+        try {
+            jobInstanceLoader.registerJob(report);
+        } catch (Exception e) {
+            LOG.error("While registering quartz job for report " + report.getKey(), e);
+
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
+            sce.getElements().add(e.getMessage());
+            throw sce;
+        }
+
+        return binder.getReportTO(report);
+    }
+
+    @PreAuthorize("hasRole('REPORT_LIST')")
+    public int count() {
+        return reportDAO.count();
+    }
+
+    @PreAuthorize("hasRole('REPORT_LIST')")
+    public List<ReportTO> list(final int page, final int size, final List<OrderByClause> orderByClauses) {
+        List<Report> reports = reportDAO.findAll(page, size, orderByClauses);
+        List<ReportTO> result = new ArrayList<>(reports.size());
+        for (Report report : reports) {
+            result.add(binder.getReportTO(report));
+        }
+        return result;
+    }
+
+    private Class<? extends ReportletConf> getReportletConfClass(final Class<Reportlet> reportletClass) {
+        Class<? extends ReportletConf> result = null;
+
+        ReportletConfClass annotation = reportletClass.getAnnotation(ReportletConfClass.class);
+        if (annotation != null) {
+            result = annotation.value();
+        }
+
+        return result;
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    private Set<Class<Reportlet>> getAllReportletClasses() {
+        Set<Class<Reportlet>> reportletClasses = new HashSet<>();
+
+        for (String className : classNamesLoader.getClassNames(ImplementationClassNamesLoader.Type.REPORTLET)) {
+            try {
+                Class reportletClass = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
+                reportletClasses.add(reportletClass);
+            } catch (ClassNotFoundException e) {
+                LOG.warn("Could not load class {}", className);
+            } catch (LinkageError e) {
+                LOG.warn("Could not link class {}", className);
+            }
+        }
+        return reportletClasses;
+    }
+
+    @PreAuthorize("hasRole('REPORT_LIST')")
+    public Set<String> getReportletConfClasses() {
+        Set<String> reportletConfClasses = new HashSet<>();
+
+        for (Class<Reportlet> reportletClass : getAllReportletClasses()) {
+            Class<? extends ReportletConf> reportletConfClass = getReportletConfClass(reportletClass);
+            if (reportletConfClass != null) {
+                reportletConfClasses.add(reportletConfClass.getName());
+            }
+        }
+
+        return reportletConfClasses;
+    }
+
+    public Class<Reportlet> findReportletClassHavingConfClass(final Class<? extends ReportletConf> reportletConfClass) {
+        Class<Reportlet> result = null;
+        for (Class<Reportlet> reportletClass : getAllReportletClasses()) {
+            Class<? extends ReportletConf> found = getReportletConfClass(reportletClass);
+            if (found != null && found.equals(reportletConfClass)) {
+                result = reportletClass;
+            }
+        }
+
+        return result;
+    }
+
+    @PreAuthorize("hasRole('REPORT_READ')")
+    public ReportTO read(final Long reportKey) {
+        Report report = reportDAO.find(reportKey);
+        if (report == null) {
+            throw new NotFoundException("Report " + reportKey);
+        }
+        return binder.getReportTO(report);
+    }
+
+    @PreAuthorize("hasRole('REPORT_READ')")
+    @Transactional(readOnly = true)
+    public ReportExecTO readExecution(final Long executionKey) {
+        ReportExec reportExec = reportExecDAO.find(executionKey);
+        if (reportExec == null) {
+            throw new NotFoundException("Report execution " + executionKey);
+        }
+        return binder.getReportExecTO(reportExec);
+    }
+
+    @PreAuthorize("hasRole('REPORT_READ')")
+    public void exportExecutionResult(final OutputStream os, final ReportExec reportExec,
+            final ReportExecExportFormat format) {
+
+        // streaming SAX handler from a compressed byte array stream
+        ByteArrayInputStream bais = new ByteArrayInputStream(reportExec.getExecResult());
+        ZipInputStream zis = new ZipInputStream(bais);
+        try {
+            // a single ZipEntry in the ZipInputStream (see ReportJob)
+            zis.getNextEntry();
+
+            Pipeline<SAXPipelineComponent> pipeline = new NonCachingPipeline<>();
+            pipeline.addComponent(new XMLGenerator(zis));
+
+            Map<String, Object> parameters = new HashMap<>();
+            parameters.put("status", reportExec.getStatus());
+            parameters.put("message", reportExec.getMessage());
+            parameters.put("startDate", reportExec.getStartDate());
+            parameters.put("endDate", reportExec.getEndDate());
+
+            switch (format) {
+                case HTML:
+                    XSLTTransformer xsl2html = new XSLTTransformer(getClass().getResource("/report/report2html.xsl"));
+                    xsl2html.setParameters(parameters);
+                    pipeline.addComponent(xsl2html);
+                    pipeline.addComponent(XMLSerializer.createXHTMLSerializer());
+                    break;
+
+                case PDF:
+                    XSLTTransformer xsl2pdf = new XSLTTransformer(getClass().getResource("/report/report2fo.xsl"));
+                    xsl2pdf.setParameters(parameters);
+                    pipeline.addComponent(xsl2pdf);
+                    pipeline.addComponent(new FopSerializer(MimeConstants.MIME_PDF));
+                    break;
+
+                case RTF:
+                    XSLTTransformer xsl2rtf = new XSLTTransformer(getClass().getResource("/report/report2fo.xsl"));
+                    xsl2rtf.setParameters(parameters);
+                    pipeline.addComponent(xsl2rtf);
+                    pipeline.addComponent(new FopSerializer(MimeConstants.MIME_RTF));
+                    break;
+
+                case CSV:
+                    XSLTTransformer xsl2csv = new XSLTTransformer(getClass().getResource("/report/report2csv.xsl"));
+                    xsl2csv.setParameters(parameters);
+                    pipeline.addComponent(xsl2csv);
+                    pipeline.addComponent(new TextSerializer());
+                    break;
+
+                case XML:
+                default:
+                    pipeline.addComponent(XMLSerializer.createXMLSerializer());
+            }
+
+            pipeline.setup(os);
+            pipeline.execute();
+
+            LOG.debug("Result of {} successfully exported as {}", reportExec, format);
+        } catch (Exception e) {
+            LOG.error("While exporting content", e);
+        } finally {
+            IOUtils.closeQuietly(zis);
+            IOUtils.closeQuietly(bais);
+        }
+    }
+
+    @PreAuthorize("hasRole('REPORT_READ')")
+    public ReportExec getAndCheckReportExec(final Long executionKey) {
+        ReportExec reportExec = reportExecDAO.find(executionKey);
+        if (reportExec == null) {
+            throw new NotFoundException("Report execution " + executionKey);
+        }
+        if (!ReportExecStatus.SUCCESS.name().equals(reportExec.getStatus()) || reportExec.getExecResult() == null) {
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidReportExec);
+            sce.getElements().add(reportExec.getExecResult() == null
+                    ? "No report data produced"
+                    : "Report did not run successfully");
+            throw sce;
+        }
+        return reportExec;
+    }
+
+    @PreAuthorize("hasRole('REPORT_EXECUTE')")
+    public ReportExecTO execute(final Long reportKey) {
+        Report report = reportDAO.find(reportKey);
+        if (report == null) {
+            throw new NotFoundException("Report " + reportKey);
+        }
+
+        try {
+            jobInstanceLoader.registerJob(report);
+
+            scheduler.getScheduler().triggerJob(
+                    new JobKey(JobNamer.getJobName(report), Scheduler.DEFAULT_GROUP));
+        } catch (Exception e) {
+            LOG.error("While executing report {}", report, e);
+
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Scheduling);
+            sce.getElements().add(e.getMessage());
+            throw sce;
+        }
+
+        ReportExecTO result = new ReportExecTO();
+        result.setReport(reportKey);
+        result.setStartDate(new Date());
+        result.setStatus(ReportExecStatus.STARTED.name());
+        result.setMessage("Job fired; waiting for results...");
+
+        return result;
+    }
+
+    @PreAuthorize("hasRole('REPORT_DELETE')")
+    public ReportTO delete(final Long reportKey) {
+        Report report = reportDAO.find(reportKey);
+        if (report == null) {
+            throw new NotFoundException("Report " + reportKey);
+        }
+
+        ReportTO deletedReport = binder.getReportTO(report);
+        jobInstanceLoader.unregisterJob(report);
+        reportDAO.delete(report);
+        return deletedReport;
+    }
+
+    @PreAuthorize("hasRole('REPORT_DELETE')")
+    public ReportExecTO deleteExecution(final Long executionKey) {
+        ReportExec reportExec = reportExecDAO.find(executionKey);
+        if (reportExec == null) {
+            throw new NotFoundException("Report execution " + executionKey);
+        }
+
+        ReportExecTO reportExecToDelete = binder.getReportExecTO(reportExec);
+        reportExecDAO.delete(reportExec);
+        return reportExecToDelete;
+    }
+
+    @Override
+    protected ReportTO resolveReference(final Method method, final Object... args)
+            throws UnresolvedReferenceException {
+
+        Long key = null;
+
+        if (ArrayUtils.isNotEmpty(args) && ("create".equals(method.getName())
+                || "update".equals(method.getName())
+                || "delete".equals(method.getName()))) {
+            for (int i = 0; key == null && i < args.length; i++) {
+                if (args[i] instanceof Long) {
+                    key = (Long) args[i];
+                } else if (args[i] instanceof ReportTO) {
+                    key = ((ReportTO) args[i]).getKey();
+                }
+            }
+        }
+
+        if ((key != null) && !key.equals(0l)) {
+            try {
+                return binder.getReportTO(reportDAO.find(key));
+            } catch (Throwable ignore) {
+                LOG.debug("Unresolved reference", ignore);
+                throw new UnresolvedReferenceException(ignore);
+            }
+        }
+
+        throw new UnresolvedReferenceException();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
----------------------------------------------------------------------
diff --git a/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
new file mode 100644
index 0000000..b85b74c
--- /dev/null
+++ b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/ResourceLogic.java
@@ -0,0 +1,273 @@
+/*
+ * 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.logic;
+
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Set;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.BulkAction;
+import org.apache.syncope.common.lib.to.BulkActionResult;
+import org.apache.syncope.common.lib.to.ConnObjectTO;
+import org.apache.syncope.common.lib.to.ResourceTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.syncope.common.lib.types.SubjectType;
+import org.apache.syncope.core.persistence.api.dao.DuplicateException;
+import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.RoleDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.AttributableUtil;
+import org.apache.syncope.core.persistence.api.entity.AttributableUtilFactory;
+import org.apache.syncope.core.persistence.api.entity.ConnInstance;
+import org.apache.syncope.core.persistence.api.entity.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.MappingItem;
+import org.apache.syncope.core.persistence.api.entity.Subject;
+import org.apache.syncope.core.provisioning.api.Connector;
+import org.apache.syncope.core.provisioning.api.ConnectorFactory;
+import org.apache.syncope.core.provisioning.api.data.ResourceDataBinder;
+import org.apache.syncope.core.misc.ConnObjectUtil;
+import org.apache.syncope.core.misc.MappingUtil;
+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.Uid;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+public class ResourceLogic extends AbstractTransactionalLogic<ResourceTO> {
+
+    @Autowired
+    private ExternalResourceDAO resourceDAO;
+
+    @Autowired
+    private UserDAO userDAO;
+
+    @Autowired
+    private RoleDAO roleDAO;
+
+    @Autowired
+    private ResourceDataBinder binder;
+
+    @Autowired
+    private ConnObjectUtil connObjectUtil;
+
+    @Autowired
+    private ConnectorFactory connFactory;
+
+    @Autowired
+    private AttributableUtilFactory attrUtilFactory;
+
+    @PreAuthorize("hasRole('RESOURCE_CREATE')")
+    public ResourceTO create(final ResourceTO resourceTO) {
+        if (StringUtils.isBlank(resourceTO.getKey())) {
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing);
+            sce.getElements().add("Resource name");
+            throw sce;
+        }
+
+        if (resourceDAO.find(resourceTO.getKey()) != null) {
+            throw new DuplicateException("Resource '" + resourceTO.getKey() + "'");
+        }
+
+        ExternalResource resource = null;
+        try {
+            resource = resourceDAO.save(binder.create(resourceTO));
+        } catch (SyncopeClientException e) {
+            throw e;
+        } catch (Exception e) {
+            SyncopeClientException ex = SyncopeClientException.build(ClientExceptionType.InvalidExternalResource);
+            ex.getElements().add(e.getMessage());
+            throw ex;
+        }
+
+        return binder.getResourceTO(resource);
+    }
+
+    @PreAuthorize("hasRole('RESOURCE_UPDATE')")
+    public ResourceTO update(final ResourceTO resourceTO) {
+        ExternalResource resource = resourceDAO.find(resourceTO.getKey());
+        if (resource == null) {
+            throw new NotFoundException("Resource '" + resourceTO.getKey() + "'");
+        }
+
+        resource = binder.update(resource, resourceTO);
+        try {
+            resource = resourceDAO.save(resource);
+        } catch (SyncopeClientException e) {
+            throw e;
+        } catch (Exception e) {
+            SyncopeClientException ex = SyncopeClientException.build(ClientExceptionType.InvalidExternalResource);
+            ex.getElements().add(e.getMessage());
+            throw ex;
+        }
+
+        return binder.getResourceTO(resource);
+    }
+
+    @PreAuthorize("hasRole('RESOURCE_DELETE')")
+    public ResourceTO delete(final String resourceName) {
+        ExternalResource resource = resourceDAO.find(resourceName);
+        if (resource == null) {
+            throw new NotFoundException("Resource '" + resourceName + "'");
+        }
+
+        ResourceTO resourceToDelete = binder.getResourceTO(resource);
+
+        resourceDAO.delete(resourceName);
+
+        return resourceToDelete;
+    }
+
+    @PreAuthorize("hasRole('RESOURCE_READ')")
+    @Transactional(readOnly = true)
+    public ResourceTO read(final String resourceName) {
+        ExternalResource resource = resourceDAO.find(resourceName);
+        if (resource == null) {
+            throw new NotFoundException("Resource '" + resourceName + "'");
+        }
+
+        return binder.getResourceTO(resource);
+    }
+
+    @PreAuthorize("isAuthenticated()")
+    @Transactional(readOnly = true)
+    public List<ResourceTO> list() {
+        return binder.getResourceTOs(resourceDAO.findAll());
+    }
+
+    @PreAuthorize("hasRole('RESOURCE_GETCONNECTOROBJECT')")
+    @Transactional(readOnly = true)
+    public ConnObjectTO getConnectorObject(final String resourceName, final SubjectType type, final Long id) {
+        ExternalResource resource = resourceDAO.find(resourceName);
+        if (resource == null) {
+            throw new NotFoundException("Resource '" + resourceName + "'");
+        }
+
+        Subject<?, ?, ?> subject = type == SubjectType.USER
+                ? userDAO.find(id)
+                : roleDAO.find(id);
+        if (subject == null) {
+            throw new NotFoundException(type + " " + id);
+        }
+
+        final AttributableUtil attrUtil = attrUtilFactory.getInstance(type.asAttributableType());
+
+        MappingItem accountIdItem = attrUtil.getAccountIdItem(resource);
+        if (accountIdItem == null) {
+            throw new NotFoundException(
+                    "AccountId mapping for " + type + " " + id + " on resource '" + resourceName + "'");
+        }
+        final String accountIdValue = MappingUtil.getAccountIdValue(
+                subject, resource, attrUtil.getAccountIdItem(resource));
+
+        final ObjectClass objectClass = SubjectType.USER == type ? ObjectClass.ACCOUNT : ObjectClass.GROUP;
+
+        final Connector connector = connFactory.getConnector(resource);
+        final ConnectorObject connectorObject = connector.getObject(objectClass, new Uid(accountIdValue),
+                connector.getOperationOptions(attrUtil.getMappingItems(resource, MappingPurpose.BOTH)));
+        if (connectorObject == null) {
+            throw new NotFoundException("Object " + accountIdValue + " with class " + objectClass
+                    + "not found on resource " + resourceName);
+        }
+
+        final Set<Attribute> attributes = connectorObject.getAttributes();
+        if (AttributeUtil.find(Uid.NAME, attributes) == null) {
+            attributes.add(connectorObject.getUid());
+        }
+        if (AttributeUtil.find(Name.NAME, attributes) == null) {
+            attributes.add(connectorObject.getName());
+        }
+
+        return connObjectUtil.getConnObjectTO(connectorObject);
+    }
+
+    @PreAuthorize("hasRole('CONNECTOR_READ')")
+    @Transactional(readOnly = true)
+    public boolean check(final ResourceTO resourceTO) {
+        final ConnInstance connInstance = binder.getConnInstance(resourceTO);
+
+        final Connector connector = connFactory.createConnector(connInstance, connInstance.getConfiguration());
+
+        boolean result;
+        try {
+            connector.test();
+            result = true;
+        } catch (Exception e) {
+            LOG.error("Test connection failure {}", e);
+            result = false;
+        }
+
+        return result;
+    }
+
+    @PreAuthorize("hasRole('RESOURCE_DELETE') and #bulkAction.operation == #bulkAction.operation.DELETE")
+    public BulkActionResult bulk(final BulkAction bulkAction) {
+        BulkActionResult res = new BulkActionResult();
+
+        if (bulkAction.getOperation() == BulkAction.Type.DELETE) {
+            for (String name : bulkAction.getTargets()) {
+                try {
+                    res.add(delete(name).getKey(), BulkActionResult.Status.SUCCESS);
+                } catch (Exception e) {
+                    LOG.error("Error performing delete for resource {}", name, e);
+                    res.add(name, BulkActionResult.Status.FAILURE);
+                }
+            }
+        }
+
+        return res;
+    }
+
+    @Override
+    protected ResourceTO resolveReference(final Method method, final Object... args)
+            throws UnresolvedReferenceException {
+
+        String key = null;
+
+        if (ArrayUtils.isNotEmpty(args)) {
+            for (int i = 0; key == null && i < args.length; i++) {
+                if (args[i] instanceof String) {
+                    key = (String) args[i];
+                } else if (args[i] instanceof ResourceTO) {
+                    key = ((ResourceTO) args[i]).getKey();
+                }
+            }
+        }
+
+        if (key != null) {
+            try {
+                return binder.getResourceTO(resourceDAO.find(key));
+            } catch (Throwable ignore) {
+                LOG.debug("Unresolved reference", ignore);
+                throw new UnresolvedReferenceException(ignore);
+            }
+        }
+
+        throw new UnresolvedReferenceException();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/RoleLogic.java
----------------------------------------------------------------------
diff --git a/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/RoleLogic.java b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/RoleLogic.java
new file mode 100644
index 0000000..ebacf04
--- /dev/null
+++ b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/RoleLogic.java
@@ -0,0 +1,405 @@
+/*
+ * 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.logic;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.Resource;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.mod.RoleMod;
+import org.apache.syncope.common.lib.to.BulkAction;
+import org.apache.syncope.common.lib.to.BulkActionResult;
+import org.apache.syncope.common.lib.to.PropagationStatus;
+import org.apache.syncope.common.lib.to.RoleTO;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.SubjectType;
+import org.apache.syncope.core.persistence.api.RoleEntitlementUtil;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.RoleDAO;
+import org.apache.syncope.core.persistence.api.dao.SubjectSearchDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.api.entity.role.Role;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.AttributableTransformer;
+import org.apache.syncope.core.provisioning.api.RoleProvisioningManager;
+import org.apache.syncope.core.provisioning.api.data.RoleDataBinder;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
+import org.apache.syncope.core.misc.security.AuthContextUtil;
+import org.apache.syncope.core.misc.security.UnauthorizedRoleException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.transaction.interceptor.TransactionInterceptor;
+
+/**
+ * Note that this controller does not extend {@link AbstractTransactionalLogic}, hence does not provide any
+ * Spring's Transactional logic at class level.
+ */
+@Component
+public class RoleLogic extends AbstractSubjectLogic<RoleTO, RoleMod> {
+
+    @Autowired
+    protected RoleDAO roleDAO;
+
+    @Autowired
+    protected UserDAO userDAO;
+
+    @Autowired
+    protected SubjectSearchDAO searchDAO;
+
+    @Autowired
+    protected RoleDataBinder binder;
+
+    @Autowired
+    protected PropagationManager propagationManager;
+
+    @Autowired
+    protected PropagationTaskExecutor taskExecutor;
+
+    @Autowired
+    protected AttributableTransformer attrTransformer;
+
+    @Resource(name = "anonymousUser")
+    protected String anonymousUser;
+
+    @Autowired
+    protected RoleProvisioningManager provisioningManager;
+
+    @PreAuthorize("hasAnyRole('ROLE_READ', T(org.apache.syncope.common.lib.SyncopeConstants).ANONYMOUS_ENTITLEMENT)")
+    @Transactional(readOnly = true)
+    @Override
+    public RoleTO read(final Long roleKey) {
+        Role role;
+        // bypass role entitlements check
+        if (anonymousUser.equals(AuthContextUtil.getAuthenticatedUsername())) {
+            role = roleDAO.find(roleKey);
+        } else {
+            role = roleDAO.authFetch(roleKey);
+        }
+
+        if (role == null) {
+            throw new NotFoundException("Role " + roleKey);
+        }
+
+        return binder.getRoleTO(role);
+    }
+
+    @PreAuthorize("isAuthenticated() "
+            + "and not(hasRole(T(org.apache.syncope.common.lib.SyncopeConstants).ANONYMOUS_ENTITLEMENT))")
+    @Transactional(readOnly = true)
+    public RoleTO readSelf(final Long roleKey) {
+        // Explicit search instead of using binder.getRoleFromId() in order to bypass auth checks - will do here
+        Role role = roleDAO.find(roleKey);
+        if (role == null) {
+            throw new NotFoundException("Role " + roleKey);
+        }
+
+        Set<Long> ownedRoleIds;
+        User authUser = userDAO.find(AuthContextUtil.getAuthenticatedUsername());
+        if (authUser == null) {
+            ownedRoleIds = Collections.<Long>emptySet();
+        } else {
+            ownedRoleIds = authUser.getRoleKeys();
+        }
+
+        Set<Long> allowedRoleIds = RoleEntitlementUtil.getRoleKeys(AuthContextUtil.getOwnedEntitlementNames());
+        allowedRoleIds.addAll(ownedRoleIds);
+        if (!allowedRoleIds.contains(role.getKey())) {
+            throw new UnauthorizedRoleException(role.getKey());
+        }
+
+        return binder.getRoleTO(role);
+    }
+
+    @PreAuthorize("hasRole('ROLE_READ')")
+    @Transactional(readOnly = true)
+    public RoleTO parent(final Long roleKey) {
+        Role role = roleDAO.authFetch(roleKey);
+
+        Set<Long> allowedRoleIds = RoleEntitlementUtil.getRoleKeys(AuthContextUtil.getOwnedEntitlementNames());
+        if (role.getParent() != null && !allowedRoleIds.contains(role.getParent().getKey())) {
+            throw new UnauthorizedRoleException(role.getParent().getKey());
+        }
+
+        RoleTO result = role.getParent() == null
+                ? null
+                : binder.getRoleTO(role.getParent());
+
+        return result;
+    }
+
+    @PreAuthorize("hasRole('ROLE_READ')")
+    @Transactional(readOnly = true)
+    public List<RoleTO> children(final Long roleKey) {
+        Role role = roleDAO.authFetch(roleKey);
+
+        Set<Long> allowedRoleIds = RoleEntitlementUtil.getRoleKeys(AuthContextUtil.getOwnedEntitlementNames());
+
+        List<Role> children = roleDAO.findChildren(role);
+        List<RoleTO> childrenTOs = new ArrayList<>(children.size());
+        for (Role child : children) {
+            if (allowedRoleIds.contains(child.getKey())) {
+                childrenTOs.add(binder.getRoleTO(child));
+            }
+        }
+
+        return childrenTOs;
+    }
+
+    @PreAuthorize("isAuthenticated()")
+    @Transactional(readOnly = true, rollbackFor = { Throwable.class })
+    @Override
+    public int count() {
+        return roleDAO.count();
+    }
+
+    @PreAuthorize("isAuthenticated()")
+    @Transactional(readOnly = true)
+    @Override
+    public List<RoleTO> list(final int page, final int size, final List<OrderByClause> orderBy) {
+        List<Role> roles = roleDAO.findAll(page, size, orderBy);
+
+        List<RoleTO> roleTOs = new ArrayList<>(roles.size());
+        for (Role role : roles) {
+            roleTOs.add(binder.getRoleTO(role));
+        }
+
+        return roleTOs;
+    }
+
+    @PreAuthorize("isAuthenticated()")
+    @Transactional(readOnly = true, rollbackFor = { Throwable.class })
+    @Override
+    public int searchCount(final SearchCond searchCondition) {
+        final Set<Long> adminRoleIds = RoleEntitlementUtil.getRoleKeys(AuthContextUtil.getOwnedEntitlementNames());
+        return searchDAO.count(adminRoleIds, searchCondition, SubjectType.ROLE);
+    }
+
+    @PreAuthorize("isAuthenticated()")
+    @Transactional(readOnly = true, rollbackFor = { Throwable.class })
+    @Override
+    public List<RoleTO> search(final SearchCond searchCondition, final int page, final int size,
+            final List<OrderByClause> orderBy) {
+
+        final List<Role> matchingRoles = searchDAO.search(
+                RoleEntitlementUtil.getRoleKeys(AuthContextUtil.getOwnedEntitlementNames()),
+                searchCondition, page, size, orderBy, SubjectType.ROLE);
+
+        final List<RoleTO> result = new ArrayList<>(matchingRoles.size());
+        for (Role role : matchingRoles) {
+            result.add(binder.getRoleTO(role));
+        }
+
+        return result;
+    }
+
+    @PreAuthorize("hasRole('ROLE_CREATE')")
+    public RoleTO create(final RoleTO roleTO) {
+        // Check that this operation is allowed to be performed by caller
+        Set<Long> allowedRoleIds = RoleEntitlementUtil.getRoleKeys(AuthContextUtil.getOwnedEntitlementNames());
+        if (roleTO.getParent() != 0 && !allowedRoleIds.contains(roleTO.getParent())) {
+            throw new UnauthorizedRoleException(roleTO.getParent());
+        }
+
+        // Attributable transformation (if configured)
+        RoleTO actual = attrTransformer.transform(roleTO);
+        LOG.debug("Transformed: {}", actual);
+
+        /*
+         * Actual operations: workflow, propagation
+         */
+        Map.Entry<Long, List<PropagationStatus>> created = provisioningManager.create(roleTO);
+        final RoleTO savedTO = binder.getRoleTO(created.getKey());
+        savedTO.getPropagationStatusTOs().addAll(created.getValue());
+        return savedTO;
+    }
+
+    @PreAuthorize("hasRole('ROLE_UPDATE')")
+    @Override
+    public RoleTO update(final RoleMod roleMod) {
+        // Check that this operation is allowed to be performed by caller
+        roleDAO.authFetch(roleMod.getKey());
+
+        // Attribute value transformation (if configured)
+        RoleMod actual = attrTransformer.transform(roleMod);
+        LOG.debug("Transformed: {}", actual);
+
+        Map.Entry<Long, List<PropagationStatus>> updated = provisioningManager.update(roleMod);
+
+        final RoleTO updatedTO = binder.getRoleTO(updated.getKey());
+        updatedTO.getPropagationStatusTOs().addAll(updated.getValue());
+        return updatedTO;
+    }
+
+    @PreAuthorize("hasRole('ROLE_DELETE')")
+    @Override
+    public RoleTO delete(final Long roleKey) {
+        List<Role> ownedRoles = roleDAO.findOwnedByRole(roleKey);
+        if (!ownedRoles.isEmpty()) {
+            List<String> owned = new ArrayList<String>(ownedRoles.size());
+            for (Role role : ownedRoles) {
+                owned.add(role.getKey() + " " + role.getName());
+            }
+
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.RoleOwnership);
+            sce.getElements().addAll(owned);
+            throw sce;
+        }
+
+        List<PropagationStatus> statuses = provisioningManager.delete(roleKey);
+
+        RoleTO roleTO = new RoleTO();
+        roleTO.setKey(roleKey);
+
+        roleTO.getPropagationStatusTOs().addAll(statuses);
+
+        return roleTO;
+    }
+
+    @PreAuthorize("(hasRole('ROLE_DELETE') and #bulkAction.operation == #bulkAction.operation.DELETE)")
+    public BulkActionResult bulk(final BulkAction bulkAction) {
+        BulkActionResult res = new BulkActionResult();
+
+        if (bulkAction.getOperation() == BulkAction.Type.DELETE) {
+            for (String roleKey : bulkAction.getTargets()) {
+                try {
+                    res.add(delete(Long.valueOf(roleKey)).getKey(), BulkActionResult.Status.SUCCESS);
+                } catch (Exception e) {
+                    LOG.error("Error performing delete for role {}", roleKey, e);
+                    res.add(roleKey, BulkActionResult.Status.FAILURE);
+                }
+            }
+        } else {
+            LOG.warn("Unsupported bulk action: {}", bulkAction.getOperation());
+        }
+
+        return res;
+    }
+
+    @PreAuthorize("hasRole('ROLE_UPDATE')")
+    @Transactional(rollbackFor = { Throwable.class })
+    @Override
+    public RoleTO unlink(final Long roleKey, final Collection<String> resources) {
+        final RoleMod roleMod = new RoleMod();
+        roleMod.setKey(roleKey);
+        roleMod.getResourcesToRemove().addAll(resources);
+        final Long updatedResult = provisioningManager.unlink(roleMod);
+
+        return binder.getRoleTO(updatedResult);
+    }
+
+    @PreAuthorize("hasRole('ROLE_UPDATE')")
+    @Transactional(rollbackFor = { Throwable.class })
+    @Override
+    public RoleTO link(final Long roleKey, final Collection<String> resources) {
+        final RoleMod roleMod = new RoleMod();
+        roleMod.setKey(roleKey);
+        roleMod.getResourcesToAdd().addAll(resources);
+        return binder.getRoleTO(provisioningManager.link(roleMod));
+    }
+
+    @PreAuthorize("hasRole('ROLE_UPDATE')")
+    @Transactional(rollbackFor = { Throwable.class })
+    @Override
+    public RoleTO unassign(final Long roleKey, final Collection<String> resources) {
+        final RoleMod roleMod = new RoleMod();
+        roleMod.setKey(roleKey);
+        roleMod.getResourcesToRemove().addAll(resources);
+        return update(roleMod);
+    }
+
+    @PreAuthorize("hasRole('ROLE_UPDATE')")
+    @Transactional(rollbackFor = { Throwable.class })
+    @Override
+    public RoleTO assign(
+            final Long roleKey, final Collection<String> resources, final boolean changePwd, final String password) {
+
+        final RoleMod userMod = new RoleMod();
+        userMod.setKey(roleKey);
+        userMod.getResourcesToAdd().addAll(resources);
+        return update(userMod);
+    }
+
+    @PreAuthorize("hasRole('ROLE_UPDATE')")
+    @Transactional(rollbackFor = { Throwable.class })
+    @Override
+    public RoleTO deprovision(final Long roleKey, final Collection<String> resources) {
+        final Role role = roleDAO.authFetch(roleKey);
+
+        List<PropagationStatus> statuses = provisioningManager.deprovision(roleKey, resources);
+
+        final RoleTO updatedTO = binder.getRoleTO(role);
+        updatedTO.getPropagationStatusTOs().addAll(statuses);
+        return updatedTO;
+    }
+
+    @PreAuthorize("hasRole('ROLE_UPDATE')")
+    @Transactional(rollbackFor = { Throwable.class })
+    @Override
+    public RoleTO provision(
+            final Long roleKey, final Collection<String> resources, final boolean changePwd, final String password) {
+        final RoleTO original = binder.getRoleTO(roleKey);
+
+        //trick: assign and retrieve propagation statuses ...
+        original.getPropagationStatusTOs().addAll(
+                assign(roleKey, resources, changePwd, password).getPropagationStatusTOs());
+
+        // .... rollback.
+        TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
+        return original;
+    }
+
+    @Override
+    protected RoleTO resolveReference(final Method method, final Object... args) throws UnresolvedReferenceException {
+        Long key = null;
+
+        if (ArrayUtils.isNotEmpty(args)) {
+            for (int i = 0; key == null && i < args.length; i++) {
+                if (args[i] instanceof Long) {
+                    key = (Long) args[i];
+                } else if (args[i] instanceof RoleTO) {
+                    key = ((RoleTO) args[i]).getKey();
+                } else if (args[i] instanceof RoleMod) {
+                    key = ((RoleMod) args[i]).getKey();
+                }
+            }
+        }
+
+        if ((key != null) && !key.equals(0l)) {
+            try {
+                return binder.getRoleTO(key);
+            } catch (Throwable ignore) {
+                LOG.debug("Unresolved reference", ignore);
+                throw new UnresolvedReferenceException(ignore);
+            }
+        }
+
+        throw new UnresolvedReferenceException();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/SchemaLogic.java
----------------------------------------------------------------------
diff --git a/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/SchemaLogic.java b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/SchemaLogic.java
new file mode 100644
index 0000000..b6e4748
--- /dev/null
+++ b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/SchemaLogic.java
@@ -0,0 +1,325 @@
+/*
+ * 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.logic;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.to.AbstractSchemaTO;
+import org.apache.syncope.common.lib.to.DerSchemaTO;
+import org.apache.syncope.common.lib.to.PlainSchemaTO;
+import org.apache.syncope.common.lib.to.VirSchemaTO;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import org.apache.syncope.common.lib.types.SchemaType;
+import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.DuplicateException;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO;
+import org.apache.syncope.core.persistence.api.entity.AttributableUtil;
+import org.apache.syncope.core.persistence.api.entity.AttributableUtilFactory;
+import org.apache.syncope.core.persistence.api.entity.DerSchema;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+import org.apache.syncope.core.persistence.api.entity.VirSchema;
+import org.apache.syncope.core.provisioning.api.data.SchemaDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+@Component
+public class SchemaLogic extends AbstractTransactionalLogic<AbstractSchemaTO> {
+
+    @Autowired
+    private PlainSchemaDAO plainSchemaDAO;
+
+    @Autowired
+    private DerSchemaDAO derSchemaDAO;
+
+    @Autowired
+    private VirSchemaDAO virSchemaDAO;
+
+    @Autowired
+    private SchemaDataBinder binder;
+
+    @Autowired
+    private AttributableUtilFactory attrUtilFactory;
+
+    private boolean doesSchemaExist(final SchemaType schemaType, final String name, final AttributableUtil attrUtil) {
+        boolean found;
+
+        switch (schemaType) {
+            case VIRTUAL:
+                found = virSchemaDAO.find(name, attrUtil.virSchemaClass()) != null;
+                break;
+
+            case DERIVED:
+                found = derSchemaDAO.find(name, attrUtil.derSchemaClass()) != null;
+                break;
+
+            case PLAIN:
+                found = plainSchemaDAO.find(name, attrUtil.plainSchemaClass()) != null;
+                break;
+
+            default:
+                found = false;
+        }
+
+        return found;
+    }
+
+    @PreAuthorize("hasRole('SCHEMA_CREATE')")
+    @SuppressWarnings("unchecked")
+    public <T extends AbstractSchemaTO> T create(
+            final AttributableType attrType, final SchemaType schemaType, final T schemaTO) {
+
+        if (StringUtils.isBlank(schemaTO.getKey())) {
+            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.RequiredValuesMissing);
+            sce.getElements().add("Schema name");
+            throw sce;
+        }
+
+        final AttributableUtil attrUtil = attrUtilFactory.getInstance(attrType);
+
+        if (doesSchemaExist(schemaType, schemaTO.getKey(), attrUtil)) {
+            throw new DuplicateException(schemaType + "/" + attrType + "/" + schemaTO.getKey());
+        }
+
+        T created;
+        switch (schemaType) {
+            case VIRTUAL:
+                VirSchema virSchema = attrUtil.newVirSchema();
+                binder.create((VirSchemaTO) schemaTO, virSchema);
+                virSchema = virSchemaDAO.save(virSchema);
+                created = (T) binder.getVirSchemaTO(virSchema);
+                break;
+            case DERIVED:
+                DerSchema derSchema = attrUtil.newDerSchema();
+                binder.create((DerSchemaTO) schemaTO, derSchema);
+                derSchema = derSchemaDAO.save(derSchema);
+
+                created = (T) binder.getDerSchemaTO(derSchema);
+                break;
+
+            case PLAIN:
+            default:
+                PlainSchema normalSchema = attrUtil.newPlainSchema();
+                binder.create((PlainSchemaTO) schemaTO, normalSchema);
+                normalSchema = plainSchemaDAO.save(normalSchema);
+
+                created = (T) binder.getSchemaTO(normalSchema, attrUtil);
+        }
+        return created;
+    }
+
+    @PreAuthorize("hasRole('SCHEMA_DELETE')")
+    public void delete(final AttributableType attrType, final SchemaType schemaType, final String schemaName) {
+        final AttributableUtil attrUtil = attrUtilFactory.getInstance(attrType);
+
+        if (!doesSchemaExist(schemaType, schemaName, attrUtil)) {
+            throw new NotFoundException(schemaType + "/" + attrType + "/" + schemaName);
+        }
+
+        switch (schemaType) {
+            case VIRTUAL:
+                virSchemaDAO.delete(schemaName, attrUtil);
+                break;
+
+            case DERIVED:
+                derSchemaDAO.delete(schemaName, attrUtil);
+                break;
+
+            case PLAIN:
+            default:
+                plainSchemaDAO.delete(schemaName, attrUtil);
+        }
+    }
+
+    @PreAuthorize("isAuthenticated()")
+    @SuppressWarnings("unchecked")
+    public <T extends AbstractSchemaTO> List<T> list(final AttributableType attrType, final SchemaType schemaType) {
+        final AttributableUtil attrUtil = attrUtilFactory.getInstance(attrType);
+
+        List<T> result;
+        switch (schemaType) {
+            case VIRTUAL:
+                List<VirSchema> virSchemas = virSchemaDAO.findAll(attrUtil.virSchemaClass());
+                result = new ArrayList<>(virSchemas.size());
+                for (VirSchema derSchema : virSchemas) {
+                    result.add((T) binder.getVirSchemaTO(derSchema));
+                }
+                break;
+
+            case DERIVED:
+                List<DerSchema> derSchemas = derSchemaDAO.findAll(attrUtil.derSchemaClass());
+                result = new ArrayList<>(derSchemas.size());
+                for (DerSchema derSchema : derSchemas) {
+                    result.add((T) binder.getDerSchemaTO(derSchema));
+                }
+                break;
+
+            case PLAIN:
+            default:
+                List<PlainSchema> schemas = plainSchemaDAO.findAll(attrUtil.plainSchemaClass());
+                result = new ArrayList<>(schemas.size());
+                for (PlainSchema schema : schemas) {
+                    result.add((T) binder.getSchemaTO(schema, attrUtil));
+                }
+        }
+
+        return result;
+    }
+
+    @PreAuthorize("hasRole('SCHEMA_READ')")
+    @SuppressWarnings("unchecked")
+    public <T extends AbstractSchemaTO> T read(
+            final AttributableType attrType, final SchemaType schemaType, final String schemaName) {
+
+        final AttributableUtil attrUtil = attrUtilFactory.getInstance(attrType);
+
+        T read;
+        switch (schemaType) {
+            case VIRTUAL:
+                VirSchema virSchema = virSchemaDAO.find(schemaName, attrUtil.virSchemaClass());
+                if (virSchema == null) {
+                    throw new NotFoundException("Virtual Schema '" + schemaName + "'");
+                }
+
+                read = (T) binder.getVirSchemaTO(virSchema);
+                break;
+
+            case DERIVED:
+                DerSchema derSchema = derSchemaDAO.find(schemaName, attrUtil.derSchemaClass());
+                if (derSchema == null) {
+                    throw new NotFoundException("Derived schema '" + schemaName + "'");
+                }
+
+                read = (T) binder.getDerSchemaTO(derSchema);
+                break;
+
+            case PLAIN:
+            default:
+                PlainSchema schema = plainSchemaDAO.find(schemaName, attrUtil.plainSchemaClass());
+                if (schema == null) {
+                    throw new NotFoundException("Schema '" + schemaName + "'");
+                }
+
+                read = (T) binder.getSchemaTO(schema, attrUtil);
+        }
+
+        return read;
+    }
+
+    @PreAuthorize("hasRole('SCHEMA_UPDATE')")
+    public <T extends AbstractSchemaTO> void update(
+            final AttributableType attrType, final SchemaType schemaType, final T schemaTO) {
+
+        final AttributableUtil attrUtil = attrUtilFactory.getInstance(attrType);
+
+        if (!doesSchemaExist(schemaType, schemaTO.getKey(), attrUtil)) {
+            throw new NotFoundException(schemaType + "/" + attrType + "/" + schemaTO.getKey());
+        }
+
+        switch (schemaType) {
+            case VIRTUAL:
+                VirSchema virSchema = virSchemaDAO.find(schemaTO.getKey(), attrUtil.virSchemaClass());
+                if (virSchema == null) {
+                    throw new NotFoundException("Virtual Schema '" + schemaTO.getKey() + "'");
+                }
+
+                binder.update((VirSchemaTO) schemaTO, virSchema);
+                virSchemaDAO.save(virSchema);
+                break;
+
+            case DERIVED:
+                DerSchema derSchema = derSchemaDAO.find(schemaTO.getKey(), attrUtil.derSchemaClass());
+                if (derSchema == null) {
+                    throw new NotFoundException("Derived schema '" + schemaTO.getKey() + "'");
+                }
+
+                binder.update((DerSchemaTO) schemaTO, derSchema);
+                derSchemaDAO.save(derSchema);
+                break;
+
+            case PLAIN:
+            default:
+                PlainSchema schema = plainSchemaDAO.find(schemaTO.getKey(), attrUtil.plainSchemaClass());
+                if (schema == null) {
+                    throw new NotFoundException("Schema '" + schemaTO.getKey() + "'");
+                }
+
+                binder.update((PlainSchemaTO) schemaTO, schema, attrUtil);
+                plainSchemaDAO.save(schema);
+        }
+    }
+
+    @Override
+    protected AbstractSchemaTO resolveReference(final Method method, final Object... args)
+            throws UnresolvedReferenceException {
+
+        String kind = null;
+        String key = null;
+        if (ArrayUtils.isNotEmpty(args)) {
+            for (int i = 0; (key == null || kind == null) && i < args.length; i++) {
+                if (args[i] instanceof String) {
+                    if (kind == null) {
+                        kind = (String) args[i];
+                    } else {
+                        key = (String) args[i];
+                    }
+                } else if (args[i] instanceof AbstractSchemaTO) {
+                    key = ((AbstractSchemaTO) args[i]).getKey();
+                }
+            }
+        }
+
+        if (key != null) {
+            try {
+                final AttributableUtil attrUtil = attrUtilFactory.getInstance(kind);
+
+                AbstractSchemaTO result = null;
+
+                PlainSchema plainSchema = plainSchemaDAO.find(key, attrUtil.plainSchemaClass());
+                if (plainSchema == null) {
+                    DerSchema derSchema = derSchemaDAO.find(key, attrUtil.derSchemaClass());
+                    if (derSchema == null) {
+                        VirSchema virSchema = virSchemaDAO.find(key, attrUtil.virSchemaClass());
+                        if (virSchema != null) {
+                            result = binder.getVirSchemaTO(virSchema);
+                        }
+                    } else {
+                        result = binder.getDerSchemaTO(derSchema);
+                    }
+                } else {
+                    result = binder.getSchemaTO(plainSchema, attrUtil);
+                }
+
+                return result;
+            } catch (Throwable ignore) {
+                LOG.debug("Unresolved reference", ignore);
+                throw new UnresolvedReferenceException(ignore);
+            }
+        }
+
+        throw new UnresolvedReferenceException();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/SecurityQuestionLogic.java
----------------------------------------------------------------------
diff --git a/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/SecurityQuestionLogic.java b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/SecurityQuestionLogic.java
new file mode 100644
index 0000000..102891f
--- /dev/null
+++ b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/SecurityQuestionLogic.java
@@ -0,0 +1,150 @@
+/*
+ * 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.logic;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.syncope.common.lib.to.SecurityQuestionTO;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
+import org.apache.syncope.core.persistence.api.dao.SecurityQuestionDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.data.SecurityQuestionDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+
+@Component
+public class SecurityQuestionLogic extends AbstractTransactionalLogic<SecurityQuestionTO> {
+
+    @Autowired
+    private SecurityQuestionDAO securityQuestionDAO;
+
+    @Autowired
+    private UserDAO userDAO;
+
+    @Autowired
+    private SecurityQuestionDataBinder binder;
+
+    @PreAuthorize("isAuthenticated()")
+    public List<SecurityQuestionTO> list() {
+        List<SecurityQuestionTO> result = new ArrayList<>();
+        for (SecurityQuestion securityQuestion : securityQuestionDAO.findAll()) {
+            result.add(binder.getSecurityQuestionTO(securityQuestion));
+        }
+
+        return result;
+    }
+
+    @PreAuthorize("isAuthenticated()")
+    public SecurityQuestionTO read(final Long securityQuestionId) {
+        SecurityQuestion securityQuestion = securityQuestionDAO.find(securityQuestionId);
+        if (securityQuestion == null) {
+            LOG.error("Could not find security question '" + securityQuestionId + "'");
+
+            throw new NotFoundException(String.valueOf(securityQuestionId));
+        }
+
+        return binder.getSecurityQuestionTO(securityQuestion);
+    }
+
+    @PreAuthorize("hasRole('SECURITY_QUESTION_CREATE')")
+    public SecurityQuestionTO create(final SecurityQuestionTO securityQuestionTO) {
+        return binder.getSecurityQuestionTO(securityQuestionDAO.save(binder.create(securityQuestionTO)));
+    }
+
+    @PreAuthorize("hasRole('SECURITY_QUESTION_UPDATE')")
+    public SecurityQuestionTO update(final SecurityQuestionTO securityQuestionTO) {
+        SecurityQuestion securityQuestion = securityQuestionDAO.find(securityQuestionTO.getKey());
+        if (securityQuestion == null) {
+            LOG.error("Could not find security question '" + securityQuestionTO.getKey() + "'");
+
+            throw new NotFoundException(String.valueOf(securityQuestionTO.getKey()));
+        }
+
+        binder.update(securityQuestion, securityQuestionTO);
+        securityQuestion = securityQuestionDAO.save(securityQuestion);
+
+        return binder.getSecurityQuestionTO(securityQuestion);
+    }
+
+    @PreAuthorize("hasRole('SECURITY_QUESTION_DELETE')")
+    public SecurityQuestionTO delete(final Long securityQuestionId) {
+        SecurityQuestion securityQuestion = securityQuestionDAO.find(securityQuestionId);
+        if (securityQuestion == null) {
+            LOG.error("Could not find security question '" + securityQuestionId + "'");
+
+            throw new NotFoundException(String.valueOf(securityQuestionId));
+        }
+
+        SecurityQuestionTO deleted = binder.getSecurityQuestionTO(securityQuestion);
+        securityQuestionDAO.delete(securityQuestionId);
+        return deleted;
+    }
+
+    @PreAuthorize("isAnonymous() or hasRole(T(org.apache.syncope.common.lib.SyncopeConstants).ANONYMOUS_ENTITLEMENT)")
+    public SecurityQuestionTO read(final String username) {
+        if (username == null) {
+            throw new NotFoundException("Null username");
+        }
+        User user = userDAO.find(username);
+        if (user == null) {
+            throw new NotFoundException("User " + username);
+        }
+
+        if (user.getSecurityQuestion() == null) {
+            LOG.error("Could not find security question for user '" + username + "'");
+
+            throw new NotFoundException("Security question for user " + username);
+        }
+
+        return binder.getSecurityQuestionTO(user.getSecurityQuestion());
+    }
+
+    @Override
+    protected SecurityQuestionTO resolveReference(final Method method, final Object... args)
+            throws UnresolvedReferenceException {
+
+        Long key = null;
+
+        if (ArrayUtils.isNotEmpty(args)) {
+            for (int i = 0; key == null && i < args.length; i++) {
+                if (args[i] instanceof Long) {
+                    key = (Long) args[i];
+                } else if (args[i] instanceof SecurityQuestionTO) {
+                    key = ((SecurityQuestionTO) args[i]).getKey();
+                }
+            }
+        }
+
+        if ((key != null) && !key.equals(0l)) {
+            try {
+                return binder.getSecurityQuestionTO(securityQuestionDAO.find(key));
+            } catch (Throwable ignore) {
+                LOG.debug("Unresolved reference", ignore);
+                throw new UnresolvedReferenceException(ignore);
+            }
+        }
+
+        throw new UnresolvedReferenceException();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
----------------------------------------------------------------------
diff --git a/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
new file mode 100644
index 0000000..153b45a
--- /dev/null
+++ b/syncope620/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
@@ -0,0 +1,168 @@
+/*
+ * 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.logic;
+
+import static org.apache.syncope.core.logic.AbstractLogic.LOG;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.util.HashSet;
+import java.util.Set;
+import javax.annotation.Resource;
+import org.apache.syncope.common.lib.to.SyncopeTO;
+import org.apache.syncope.core.logic.init.ImplementationClassNamesLoader;
+import org.apache.syncope.core.misc.spring.ResourceWithFallbackLoader;
+import org.apache.syncope.core.persistence.api.dao.ConfDAO;
+import org.apache.syncope.core.provisioning.api.AttributableTransformer;
+import org.apache.syncope.core.provisioning.api.ConnIdBundleManager;
+import org.apache.syncope.core.provisioning.api.RoleProvisioningManager;
+import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
+import org.apache.syncope.core.provisioning.java.notification.NotificationManagerImpl;
+import org.apache.syncope.core.workflow.api.RoleWorkflowAdapter;
+import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
+import org.springframework.aop.support.AopUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+@Component
+public class SyncopeLogic extends AbstractLogic<SyncopeTO> {
+
+    @Autowired
+    private ConfDAO confDAO;
+
+    @Resource(name = "version")
+    private String version;
+
+    @Autowired
+    private ConnIdBundleManager bundleManager;
+
+    @Autowired
+    private AttributableTransformer attrTransformer;
+
+    @Autowired
+    private UserWorkflowAdapter uwfAdapter;
+
+    @Autowired
+    private RoleWorkflowAdapter rwfAdapter;
+
+    @Autowired
+    private UserProvisioningManager uProvisioningManager;
+
+    @Autowired
+    private RoleProvisioningManager rProvisioningManager;
+
+    @Autowired
+    private ImplementationClassNamesLoader classNamesLoader;
+
+    @Resource(name = "velocityResourceLoader")
+    private ResourceWithFallbackLoader resourceLoader;
+
+    @Transactional(readOnly = true)
+    public boolean isSelfRegAllowed() {
+        return confDAO.find("selfRegistration.allowed", "false").getValues().get(0).getBooleanValue();
+    }
+
+    @Transactional(readOnly = true)
+    public boolean isPwdResetAllowed() {
+        return confDAO.find("passwordReset.allowed", "false").getValues().get(0).getBooleanValue();
+    }
+
+    @Transactional(readOnly = true)
+    public boolean isPwdResetRequiringSecurityQuestions() {
+        return confDAO.find("passwordReset.securityQuestion", "true").getValues().get(0).getBooleanValue();
+    }
+
+    @PreAuthorize("isAuthenticated()")
+    @Transactional(readOnly = true)
+    public SyncopeTO info() {
+        SyncopeTO syncopeTO = new SyncopeTO();
+        syncopeTO.setVersion(version);
+
+        syncopeTO.setSelfRegAllowed(isSelfRegAllowed());
+        syncopeTO.setPwdResetAllowed(isPwdResetAllowed());
+        syncopeTO.setPwdResetRequiringSecurityQuestions(isPwdResetRequiringSecurityQuestions());
+
+        if (bundleManager.getLocations() != null) {
+            for (URI location : bundleManager.getLocations()) {
+                syncopeTO.getConnIdLocations().add(location.toASCIIString());
+            }
+        }
+
+        syncopeTO.setAttributableTransformer(attrTransformer.getClass().getName());
+
+        syncopeTO.setUserWorkflowAdapter(AopUtils.getTargetClass(uwfAdapter).getName());
+        syncopeTO.setRoleWorkflowAdapter(AopUtils.getTargetClass(rwfAdapter).getName());
+
+        syncopeTO.setUserProvisioningManager(uProvisioningManager.getClass().getName());
+        syncopeTO.setRoleProvisioningManager(rProvisioningManager.getClass().getName());
+
+        syncopeTO.getReportlets().addAll(
+                classNamesLoader.getClassNames(ImplementationClassNamesLoader.Type.REPORTLET));
+        syncopeTO.getTaskJobs().addAll(
+                classNamesLoader.getClassNames(ImplementationClassNamesLoader.Type.TASKJOB));
+        syncopeTO.getPropagationActions().addAll(
+                classNamesLoader.getClassNames(ImplementationClassNamesLoader.Type.PROPAGATION_ACTIONS));
+        syncopeTO.getSyncActions().addAll(
+                classNamesLoader.getClassNames(ImplementationClassNamesLoader.Type.SYNC_ACTIONS));
+        syncopeTO.getPushActions().addAll(
+                classNamesLoader.getClassNames(ImplementationClassNamesLoader.Type.PUSH_ACTIONS));
+        syncopeTO.getSyncCorrelationRules().addAll(
+                classNamesLoader.getClassNames(ImplementationClassNamesLoader.Type.SYNC_CORRELATION_RULE));
+        syncopeTO.getPushCorrelationRules().addAll(
+                classNamesLoader.getClassNames(ImplementationClassNamesLoader.Type.PUSH_CORRELATION_RULE));
+        syncopeTO.getValidators().addAll(
+                classNamesLoader.getClassNames(ImplementationClassNamesLoader.Type.VALIDATOR));
+
+        Set<String> htmlTemplates = new HashSet<>();
+        Set<String> textTemplates = new HashSet<>();
+        try {
+            for (org.springframework.core.io.Resource resource : resourceLoader.getResources(
+                    NotificationManagerImpl.MAIL_TEMPLATES + "*.vm")) {
+
+                String template = resource.getURL().toExternalForm();
+                if (template.endsWith(NotificationManagerImpl.MAIL_TEMPLATE_HTML_SUFFIX)) {
+                    htmlTemplates.add(template.substring(template.indexOf(NotificationManagerImpl.MAIL_TEMPLATES) + 14,
+                            template.indexOf(NotificationManagerImpl.MAIL_TEMPLATE_HTML_SUFFIX)));
+                } else if (template.endsWith(NotificationManagerImpl.MAIL_TEMPLATE_TEXT_SUFFIX)) {
+                    textTemplates.add(template.substring(template.indexOf(NotificationManagerImpl.MAIL_TEMPLATES) + 14,
+                            template.indexOf(NotificationManagerImpl.MAIL_TEMPLATE_TEXT_SUFFIX)));
+                } else {
+                    LOG.warn("Unexpected template found: {}, ignoring...", template);
+                }
+            }
+        } catch (IOException e) {
+            LOG.error("While searching for mail templates", e);
+        }
+        // Only templates available both as HTML and TEXT are considered
+        htmlTemplates.retainAll(textTemplates);
+        syncopeTO.getMailTemplates().addAll(htmlTemplates);
+
+        return syncopeTO;
+    }
+
+    @Override
+    protected SyncopeTO resolveReference(final Method method, final Object... args)
+            throws UnresolvedReferenceException {
+
+        throw new UnresolvedReferenceException();
+    }
+}