You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by md...@apache.org on 2015/08/27 13:12:28 UTC

[01/33] syncope git commit: Fixing a "focus" bug when searching roles. Currently, hitting "enter" on the final textfield doesn't search, but switches tabs instead.

Repository: syncope
Updated Branches:
  refs/heads/SYNCOPE-156 bbb94a99f -> d9437d37a


Fixing a "focus" bug when searching roles. Currently, hitting "enter" on the final textfield doesn't search, but switches tabs instead.

Conflicts:
	console/src/main/java/org/apache/syncope/console/pages/Roles.java


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

Branch: refs/heads/SYNCOPE-156
Commit: dce78dbd08cd5d29334485337c5282b1e8f0e6d1
Parents: 3621adf
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Fri May 15 16:35:27 2015 +0100
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Fri May 15 16:49:36 2015 +0100

----------------------------------------------------------------------
 .../org/apache/syncope/console/pages/Roles.java | 33 +++++++++++---------
 1 file changed, 18 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/dce78dbd/console/src/main/java/org/apache/syncope/console/pages/Roles.java
----------------------------------------------------------------------
diff --git a/console/src/main/java/org/apache/syncope/console/pages/Roles.java b/console/src/main/java/org/apache/syncope/console/pages/Roles.java
index b691073..563c2ee 100644
--- a/console/src/main/java/org/apache/syncope/console/pages/Roles.java
+++ b/console/src/main/java/org/apache/syncope/console/pages/Roles.java
@@ -104,26 +104,29 @@ public class Roles extends BasePage {
         final RoleSearchPanel searchPanel = new RoleSearchPanel.Builder("searchPanel").build();
         searchForm.add(searchPanel);
 
-        searchForm.add(new ClearIndicatingAjaxButton("search", new ResourceModel("search"), getPageReference()) {
+        final ClearIndicatingAjaxButton searchButton =
+                new ClearIndicatingAjaxButton("search", new ResourceModel("search"), getPageReference()) {
 
-            private static final long serialVersionUID = -958724007591692537L;
+                private static final long serialVersionUID = -958724007591692537L;
 
-            @Override
-            protected void onSubmitInternal(final AjaxRequestTarget target, final Form<?> form) {
-                final NodeCond searchCond = searchPanel.buildSearchCond();
-                LOG.debug("Node condition {}", searchCond);
+                @Override
+                protected void onSubmitInternal(final AjaxRequestTarget target, final Form<?> form) {
+                    final NodeCond searchCond = searchPanel.buildSearchCond();
+                    LOG.debug("Node condition {}", searchCond);
 
-                doSearch(target, searchCond, searchResult);
+                    doSearch(target, searchCond, searchResult);
 
-                Session.get().getFeedbackMessages().clear();
-                target.add(searchPanel.getSearchFeedback());
-            }
+                    Session.get().getFeedbackMessages().clear();
+                    target.add(searchPanel.getSearchFeedback());
+                }
 
-            @Override
-            protected void onError(final AjaxRequestTarget target, final Form<?> form) {
-                target.add(searchPanel.getSearchFeedback());
-            }
-        });
+                @Override
+                protected void onError(final AjaxRequestTarget target, final Form<?> form) {
+                    target.add(searchPanel.getSearchFeedback());
+                }
+            };
+        searchForm.add(searchButton);
+        searchForm.setDefaultButton(searchButton);
     }
 
     private void doSearch(final AjaxRequestTarget target, final NodeCond searchCond,


[06/33] syncope git commit: Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/syncope

Posted by md...@apache.org.
Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/syncope


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

Branch: refs/heads/SYNCOPE-156
Commit: 850269800611173a97c178bf90657312580a45e7
Parents: 56568a4 9abd35a
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Wed Aug 19 09:19:21 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Wed Aug 19 09:19:21 2015 +0200

----------------------------------------------------------------------
 .../client/console/panels/ListViewPanel.java    |  68 ++++--
 .../console/panels/NotificationPanel.java       |  19 +-
 .../console/panels/ResourceDetailsPanel.java    |   2 +-
 .../console/panels/ResourceMappingPanel.java    |  48 ----
 .../client/console/panels/ResourceModal.java    |  25 ++-
 .../client/console/rest/AnyTypeRestClient.java  |  46 ++++
 .../client/console/topology/Topology.java       |  79 ++-----
 .../console/topology/WebSocketBehavior.java     | 209 +++++++++++++++++
 .../markup/html/form/AjaxCheckBoxPanel.java     |   7 +-
 .../html/form/AjaxDropDownChoicePanel.java      |   7 +-
 .../markup/html/form/AjaxTextFieldPanel.java    |   7 +-
 .../wicket/markup/html/form/FieldPanel.java     |   8 +-
 .../client/console/wizards/AjaxWizard.java      |  79 +++++--
 .../console/wizards/AjaxWizardBuilder.java      |  96 ++++++++
 .../console/wizards/AjaxWizardButtonBar.java    |  28 ++-
 .../client/console/wizards/ProvisionWizard.java | 123 ----------
 .../provision/ProvisionWizardBuilder.java       | 222 +++++++++++++++++++
 .../console/panels/ResourceMappingPanel.html    |  20 --
 .../wizards/ProvisionWizard$AccountLink.html    |  23 --
 .../wizards/ProvisionWizard$Mapping.html        |  23 --
 .../wizards/ProvisionWizard$ObjectType.html     |  30 ---
 .../console/wizards/ProvisionWizard.properties  |  24 --
 .../wizards/ProvisionWizard_it.properties       |  24 --
 .../wizards/ProvisionWizard_pt_BR.properties    |  24 --
 .../ProvisionWizardBuilder$ConnObjectLink.html  |  31 +++
 ...isionWizardBuilder$ConnObjectLink.properties |  20 ++
 ...onWizardBuilder$ConnObjectLink_it.properties |  20 ++
 ...izardBuilder$ConnObjectLink_pt_BR.properties |  20 ++
 .../ProvisionWizardBuilder$Mapping.html         |  23 ++
 .../ProvisionWizardBuilder$Mapping.properties   |  20 ++
 ...ProvisionWizardBuilder$Mapping_it.properties |  20 ++
 ...visionWizardBuilder$Mapping_pt_BR.properties |  20 ++
 .../ProvisionWizardBuilder$ObjectType.html      |  31 +++
 ...ProvisionWizardBuilder$ObjectType.properties |  24 ++
 ...visionWizardBuilder$ObjectType_it.properties |  23 ++
 ...ionWizardBuilder$ObjectType_pt_BR.properties |  23 ++
 36 files changed, 1042 insertions(+), 474 deletions(-)
----------------------------------------------------------------------



[25/33] syncope git commit: [SYNCOPE-686] Merge from 1_2_X

Posted by md...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/054ea9ca/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/UserSelfITCase.java
----------------------------------------------------------------------
diff --cc fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/UserSelfITCase.java
index 34a9349,0000000..78eb504
mode 100644,000000..100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/UserSelfITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/UserSelfITCase.java
@@@ -1,354 -1,0 +1,354 @@@
 +/*
 + * 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.fit.core.reference;
 +
 +import static org.junit.Assert.assertEquals;
 +import static org.junit.Assert.assertFalse;
 +import static org.junit.Assert.assertNotEquals;
 +import static org.junit.Assert.assertNotNull;
 +import static org.junit.Assert.assertNull;
 +import static org.junit.Assert.assertTrue;
 +import static org.junit.Assert.fail;
 +
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.security.AccessControlException;
 +import java.util.Map;
 +import java.util.Set;
 +import javax.ws.rs.core.Response;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.commons.lang3.tuple.Pair;
 +import org.apache.cxf.helpers.IOUtils;
 +import org.apache.syncope.client.lib.SyncopeClient;
 +import org.apache.syncope.common.lib.SyncopeClientException;
 +import org.apache.syncope.common.lib.mod.StatusMod;
 +import org.apache.syncope.common.lib.mod.UserMod;
 +import org.apache.syncope.common.lib.to.MembershipTO;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.to.WorkflowFormPropertyTO;
 +import org.apache.syncope.common.lib.to.WorkflowFormTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.common.lib.types.ClientExceptionType;
 +import org.apache.syncope.common.rest.api.Preference;
 +import org.apache.syncope.common.rest.api.RESTHeaders;
 +import org.apache.syncope.common.rest.api.service.UserSelfService;
 +import org.apache.syncope.common.rest.api.service.UserService;
 +import org.junit.Assume;
 +import org.junit.FixMethodOrder;
 +import org.junit.Test;
 +import org.junit.runners.MethodSorters;
 +import org.springframework.jdbc.core.JdbcTemplate;
 +
 +@FixMethodOrder(MethodSorters.JVM)
 +public class UserSelfITCase extends AbstractITCase {
 +
 +    @Test
 +    public void selfRegistrationAllowed() {
 +        assertTrue(syncopeService.info().isSelfRegAllowed());
 +    }
 +
 +    @Test
 +    public void create() {
 +        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers(syncopeService));
 +
 +        // 1. self-registration as admin: failure
 +        try {
 +            userSelfService.create(UserITCase.getUniqueSampleTO("anonymous@syncope.apache.org"), true);
 +            fail();
 +        } catch (AccessControlException e) {
 +            assertNotNull(e);
 +        }
 +
 +        // 2. self-registration as anonymous: works
 +        SyncopeClient anonClient = clientFactory.createAnonymous();
 +        UserTO self = anonClient.getService(UserSelfService.class).
 +                create(UserITCase.getUniqueSampleTO("anonymous@syncope.apache.org"), true).
 +                readEntity(UserTO.class);
 +        assertNotNull(self);
 +        assertEquals("createApproval", self.getStatus());
 +    }
 +
 +    @Test
 +    public void createAndApprove() {
 +        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers(syncopeService));
 +
 +        // self-create user with membership: goes 'createApproval' with resources and membership but no propagation
 +        UserTO userTO = UserITCase.getUniqueSampleTO("anonymous@syncope.apache.org");
 +        MembershipTO membership = new MembershipTO();
 +        membership.setRightKey(3L);
 +        userTO.getMemberships().add(membership);
 +        userTO.getResources().add(RESOURCE_NAME_TESTDB);
 +
 +        SyncopeClient anonClient = clientFactory.createAnonymous();
 +        userTO = anonClient.getService(UserSelfService.class).
 +                create(userTO, true).
 +                readEntity(UserTO.class);
 +        assertNotNull(userTO);
 +        assertEquals("createApproval", userTO.getStatus());
 +        assertFalse(userTO.getMemberships().isEmpty());
 +        assertFalse(userTO.getResources().isEmpty());
 +
 +        try {
 +            resourceService.readConnObject(RESOURCE_NAME_TESTDB, AnyTypeKind.USER.name(), userTO.getKey());
 +            fail();
 +        } catch (SyncopeClientException e) {
 +            assertEquals(ClientExceptionType.NotFound, e.getType());
 +        }
 +
 +        // now approve and verify that propagation has happened
 +        WorkflowFormTO form = userWorkflowService.getFormForUser(userTO.getKey());
 +        form = userWorkflowService.claimForm(form.getTaskId());
 +        Map<String, WorkflowFormPropertyTO> props = form.getPropertyMap();
 +        props.get("approve").setValue(Boolean.TRUE.toString());
 +        form.getProperties().clear();
 +        form.getProperties().addAll(props.values());
 +        userTO = userWorkflowService.submitForm(form);
 +        assertNotNull(userTO);
 +        assertEquals("active", userTO.getStatus());
 +        assertNotNull(resourceService.readConnObject(RESOURCE_NAME_TESTDB, AnyTypeKind.USER.name(), userTO.getKey()));
 +    }
 +
 +    @Test
 +    public void read() {
 +        UserService userService2 = clientFactory.create("rossini", ADMIN_PWD).getService(UserService.class);
 +
 +        try {
 +            userService2.read(1L);
 +            fail();
 +        } catch (AccessControlException e) {
 +            assertNotNull(e);
 +        }
 +
 +        Pair<Map<String, Set<String>>, UserTO> self = clientFactory.create("rossini", ADMIN_PWD).self();
 +        assertEquals("rossini", self.getValue().getUsername());
 +    }
 +
 +    @Test
 +    public void updateWithoutApproval() {
 +        // 1. create user as admin
 +        UserTO created = createUser(UserITCase.getUniqueSampleTO("anonymous@syncope.apache.org"));
 +        assertNotNull(created);
 +        assertFalse(created.getUsername().endsWith("XX"));
 +
 +        // 2. self-update (username) - works
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(created.getKey());
 +        userMod.setUsername(created.getUsername() + "XX");
 +
 +        SyncopeClient authClient = clientFactory.create(created.getUsername(), "password123");
 +        UserTO updated = authClient.getService(UserSelfService.class).update(userMod).
 +                readEntity(UserTO.class);
 +        assertNotNull(updated);
 +        assertEquals(ActivitiDetector.isActivitiEnabledForUsers(syncopeService)
 +                ? "active" : "created", updated.getStatus());
 +        assertTrue(updated.getUsername().endsWith("XX"));
 +    }
 +
 +    @Test
 +    public void updateWithApproval() {
 +        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers(syncopeService));
 +
 +        // 1. create user as admin
 +        UserTO created = createUser(UserITCase.getUniqueSampleTO("anonymous@syncope.apache.org"));
 +        assertNotNull(created);
 +        assertFalse(created.getUsername().endsWith("XX"));
 +
 +        // 2. self-update (username + memberships + resource) - works but needs approval
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(created.getKey());
 +        userMod.setUsername(created.getUsername() + "XX");
 +        userMod.getMembershipsToAdd().add(7L);
 +        userMod.getResourcesToAdd().add(RESOURCE_NAME_TESTDB);
 +        userMod.setPassword("newPassword123");
 +        StatusMod statusMod = new StatusMod();
 +        statusMod.setOnSyncope(false);
-         statusMod.getResourceNames().add(RESOURCE_NAME_TESTDB);
++        statusMod.getResources().add(RESOURCE_NAME_TESTDB);
 +        userMod.setPwdPropRequest(statusMod);
 +
 +        SyncopeClient authClient = clientFactory.create(created.getUsername(), "password123");
 +        UserTO updated = authClient.getService(UserSelfService.class).update(userMod).
 +                readEntity(UserTO.class);
 +        assertNotNull(updated);
 +        assertEquals("updateApproval", updated.getStatus());
 +        assertFalse(updated.getUsername().endsWith("XX"));
 +        assertTrue(updated.getMemberships().isEmpty());
 +
 +        // no propagation happened
 +        assertTrue(updated.getResources().isEmpty());
 +        try {
 +            resourceService.readConnObject(RESOURCE_NAME_TESTDB, AnyTypeKind.USER.name(), updated.getKey());
 +            fail();
 +        } catch (SyncopeClientException e) {
 +            assertEquals(ClientExceptionType.NotFound, e.getType());
 +        }
 +
 +        // 3. approve self-update as admin
 +        WorkflowFormTO form = userWorkflowService.getFormForUser(updated.getKey());
 +        form = userWorkflowService.claimForm(form.getTaskId());
 +        Map<String, WorkflowFormPropertyTO> props = form.getPropertyMap();
 +        props.get("approve").setValue(Boolean.TRUE.toString());
 +        form.getProperties().clear();
 +        form.getProperties().addAll(props.values());
 +        updated = userWorkflowService.submitForm(form);
 +        assertNotNull(updated);
 +        assertEquals("active", updated.getStatus());
 +        assertTrue(updated.getUsername().endsWith("XX"));
 +        assertEquals(1, updated.getMemberships().size());
 +
 +        // check that propagation also happened
 +        assertTrue(updated.getResources().contains(RESOURCE_NAME_TESTDB));
 +        assertNotNull(resourceService.readConnObject(RESOURCE_NAME_TESTDB, AnyTypeKind.USER.name(), updated.getKey()));
 +    }
 +
 +    @Test
 +    public void delete() {
 +        UserTO created = createUser(UserITCase.getUniqueSampleTO("anonymous@syncope.apache.org"));
 +        assertNotNull(created);
 +
 +        SyncopeClient authClient = clientFactory.create(created.getUsername(), "password123");
 +        UserTO deleted = authClient.getService(UserSelfService.class).delete().readEntity(UserTO.class);
 +        assertNotNull(deleted);
 +        assertEquals(ActivitiDetector.isActivitiEnabledForUsers(syncopeService)
 +                ? "deleteApproval" : null, deleted.getStatus());
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE373() {
 +        UserTO userTO = adminClient.self().getValue();
 +        assertEquals(ADMIN_UNAME, userTO.getUsername());
 +    }
 +
 +    @Test
 +    public void noContent() throws IOException {
 +        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers(syncopeService));
 +
 +        SyncopeClient anonClient = clientFactory.createAnonymous();
 +        UserSelfService noContentService = anonClient.prefer(UserSelfService.class, Preference.RETURN_NO_CONTENT);
 +
 +        UserTO user = UserITCase.getUniqueSampleTO("nocontent-anonymous@syncope.apache.org");
 +
 +        Response response = noContentService.create(user, true);
 +        assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
 +        assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
 +        assertEquals(StringUtils.EMPTY, IOUtils.toString((InputStream) response.getEntity()));
 +    }
 +
 +    @Test
 +    public void passwordReset() {
 +        // 0. ensure that password request DOES require security question
 +        configurationService.set(attrTO("passwordReset.securityQuestion", "true"));
 +
 +        // 1. create an user with security question and answer
 +        UserTO user = UserITCase.getUniqueSampleTO("pwdReset@syncope.apache.org");
 +        user.setSecurityQuestion(1L);
 +        user.setSecurityAnswer("Rossi");
 +        user.getResources().add(RESOURCE_NAME_TESTDB);
 +        createUser(user);
 +
 +        // verify propagation (including password) on external db
 +        JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
 +        String pwdOnResource = jdbcTemplate.queryForObject("SELECT password FROM test WHERE id=?", String.class,
 +                user.getUsername());
 +        assertTrue(StringUtils.isNotBlank(pwdOnResource));
 +
 +        // 2. verify that new user is able to authenticate
 +        SyncopeClient authClient = clientFactory.create(user.getUsername(), "password123");
 +        UserTO read = authClient.self().getValue();
 +        assertNotNull(read);
 +
 +        // 3. request password reset (as anonymous) providing the expected security answer
 +        SyncopeClient anonClient = clientFactory.createAnonymous();
 +        try {
 +            anonClient.getService(UserSelfService.class).requestPasswordReset(user.getUsername(), "WRONG");
 +            fail();
 +        } catch (SyncopeClientException e) {
 +            assertEquals(ClientExceptionType.InvalidSecurityAnswer, e.getType());
 +        }
 +        anonClient.getService(UserSelfService.class).requestPasswordReset(user.getUsername(), "Rossi");
 +
 +        // 4. get token (normally sent via e-mail, now reading as admin)
 +        String token = userService.read(read.getKey()).getToken();
 +        assertNotNull(token);
 +
 +        // 5. confirm password reset
 +        try {
 +            anonClient.getService(UserSelfService.class).confirmPasswordReset("WRONG TOKEN", "newPassword");
 +            fail();
 +        } catch (SyncopeClientException e) {
 +            assertEquals(ClientExceptionType.NotFound, e.getType());
 +            assertTrue(e.getMessage().contains("WRONG TOKEN"));
 +        }
 +        anonClient.getService(UserSelfService.class).confirmPasswordReset(token, "newPassword123");
 +
 +        // 6. verify that password was reset and token removed
 +        authClient = clientFactory.create(user.getUsername(), "newPassword123");
 +        read = authClient.self().getValue();
 +        assertNotNull(read);
 +        assertNull(read.getToken());
 +
 +        // 7. verify that password was changed on external resource
 +        String newPwdOnResource = jdbcTemplate.queryForObject("SELECT password FROM test WHERE id=?", String.class,
 +                user.getUsername());
 +        assertTrue(StringUtils.isNotBlank(newPwdOnResource));
 +        assertNotEquals(pwdOnResource, newPwdOnResource);
 +    }
 +
 +    @Test
 +    public void passwordResetWithoutSecurityQuestion() {
 +        // 0. disable security question for password reset
 +        configurationService.set(attrTO("passwordReset.securityQuestion", "false"));
 +
 +        // 1. create an user with security question and answer
 +        UserTO user = UserITCase.getUniqueSampleTO("pwdResetNoSecurityQuestion@syncope.apache.org");
 +        createUser(user);
 +
 +        // 2. verify that new user is able to authenticate
 +        SyncopeClient authClient = clientFactory.create(user.getUsername(), "password123");
 +        UserTO read = authClient.self().getValue();
 +        assertNotNull(read);
 +
 +        // 3. request password reset (as anonymous) with no security answer
 +        SyncopeClient anonClient = clientFactory.createAnonymous();
 +        anonClient.getService(UserSelfService.class).requestPasswordReset(user.getUsername(), null);
 +
 +        // 4. get token (normally sent via e-mail, now reading as admin)
 +        String token = userService.read(read.getKey()).getToken();
 +        assertNotNull(token);
 +
 +        // 5. confirm password reset
 +        try {
 +            anonClient.getService(UserSelfService.class).confirmPasswordReset("WRONG TOKEN", "newPassword");
 +            fail();
 +        } catch (SyncopeClientException e) {
 +            assertEquals(ClientExceptionType.NotFound, e.getType());
 +            assertTrue(e.getMessage().contains("WRONG TOKEN"));
 +        }
 +        anonClient.getService(UserSelfService.class).confirmPasswordReset(token, "newPassword123");
 +
 +        // 6. verify that password was reset and token removed
 +        authClient = clientFactory.create(user.getUsername(), "newPassword123");
 +        read = authClient.self().getValue();
 +        assertNotNull(read);
 +        assertNull(read.getToken());
 +
 +        // 7. re-enable security question for password reset
 +        configurationService.set(attrTO("passwordReset.securityQuestion", "true"));
 +    }
 +
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/054ea9ca/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/VirAttrITCase.java
----------------------------------------------------------------------
diff --cc fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/VirAttrITCase.java
index 5644447,0000000..cb166a9
mode 100644,000000..100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/VirAttrITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/VirAttrITCase.java
@@@ -1,616 -1,0 +1,616 @@@
 +/*
 + * 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.fit.core.reference;
 +
 +import static org.junit.Assert.assertEquals;
 +import static org.junit.Assert.assertFalse;
 +import static org.junit.Assert.assertNotNull;
 +import static org.junit.Assert.assertTrue;
 +
 +import java.util.Collections;
 +import java.util.Locale;
 +import java.util.Map;
 +import org.apache.commons.lang3.SerializationUtils;
 +import org.apache.syncope.common.lib.mod.AttrMod;
 +import org.apache.syncope.common.lib.mod.StatusMod;
 +import org.apache.syncope.common.lib.mod.UserMod;
 +import org.apache.syncope.common.lib.to.AttrTO;
 +import org.apache.syncope.common.lib.to.ConnInstanceTO;
 +import org.apache.syncope.common.lib.to.ConnObjectTO;
 +import org.apache.syncope.common.lib.to.MappingItemTO;
 +import org.apache.syncope.common.lib.to.MappingTO;
 +import org.apache.syncope.common.lib.to.MembershipTO;
 +import org.apache.syncope.common.lib.to.ResourceTO;
 +import org.apache.syncope.common.lib.to.GroupTO;
 +import org.apache.syncope.common.lib.to.ProvisionTO;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.common.lib.types.ConnConfProperty;
 +import org.apache.syncope.common.lib.types.IntMappingType;
 +import org.apache.syncope.common.lib.types.MappingPurpose;
 +import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
 +import org.apache.syncope.common.rest.api.service.ResourceService;
 +import org.identityconnectors.framework.common.objects.ObjectClass;
 +import org.junit.FixMethodOrder;
 +import org.junit.Test;
 +import org.junit.runners.MethodSorters;
 +import org.springframework.jdbc.core.JdbcTemplate;
 +
 +@FixMethodOrder(MethodSorters.JVM)
 +public class VirAttrITCase extends AbstractITCase {
 +
 +    @Test
 +    public void issueSYNCOPE16() {
 +        UserTO userTO = UserITCase.getUniqueSampleTO("issue16@apache.org");
 +
 +        MembershipTO membershipTO = new MembershipTO();
 +        membershipTO.setRightKey(8L);
 +        userTO.getMemberships().add(membershipTO);
 +
 +        // 1. create user
 +        UserTO actual = createUser(userTO);
 +        assertNotNull(actual);
 +
 +        // 2. check for virtual attribute value
 +        actual = userService.read(actual.getKey());
 +        assertNotNull(actual);
 +        assertEquals("virtualvalue", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
 +
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(actual.getKey());
 +        userMod.getVirAttrsToRemove().add("virtualdata");
 +        userMod.getVirAttrsToUpdate().add(attrMod("virtualdata", "virtualupdated"));
 +
 +        // 3. update virtual attribute
 +        actual = updateUser(userMod);
 +        assertNotNull(actual);
 +
 +        // 4. check for virtual attribute value
 +        actual = userService.read(actual.getKey());
 +        assertNotNull(actual);
 +        assertEquals("virtualupdated", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE260() {
 +        // ----------------------------------
 +        // create user and check virtual attribute value propagation
 +        // ----------------------------------
 +        UserTO userTO = UserITCase.getUniqueSampleTO("260@a.com");
 +        userTO.getResources().add(RESOURCE_NAME_WS2);
 +
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +        assertFalse(userTO.getPropagationStatusTOs().isEmpty());
 +        assertEquals(RESOURCE_NAME_WS2, userTO.getPropagationStatusTOs().get(0).getResource());
 +        assertEquals(PropagationTaskExecStatus.SUBMITTED, userTO.getPropagationStatusTOs().get(0).getStatus());
 +
 +        ConnObjectTO connObjectTO =
 +                resourceService.readConnObject(RESOURCE_NAME_WS2, AnyTypeKind.USER.name(), userTO.getKey());
 +        assertNotNull(connObjectTO);
 +        assertEquals("virtualvalue", connObjectTO.getPlainAttrMap().get("NAME").getValues().get(0));
 +        // ----------------------------------
 +
 +        // ----------------------------------
 +        // update user virtual attribute and check virtual attribute value update propagation
 +        // ----------------------------------
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +
 +        AttrMod attrMod = new AttrMod();
 +        attrMod.setSchema("virtualdata");
 +        attrMod.getValuesToBeRemoved().add("virtualvalue");
 +        attrMod.getValuesToBeAdded().add("virtualvalue2");
 +
 +        userMod.getVirAttrsToUpdate().add(attrMod);
 +
 +        userTO = updateUser(userMod);
 +        assertNotNull(userTO);
 +        assertFalse(userTO.getPropagationStatusTOs().isEmpty());
 +        assertEquals("ws-target-resource-2", userTO.getPropagationStatusTOs().get(0).getResource());
 +        assertEquals(PropagationTaskExecStatus.SUBMITTED, userTO.getPropagationStatusTOs().get(0).getStatus());
 +
 +        connObjectTO = resourceService.readConnObject(RESOURCE_NAME_WS2, AnyTypeKind.USER.name(), userTO.getKey());
 +        assertNotNull(connObjectTO);
 +        assertEquals("virtualvalue2", connObjectTO.getPlainAttrMap().get("NAME").getValues().get(0));
 +        // ----------------------------------
 +
 +        // ----------------------------------
 +        // suspend/reactivate user and check virtual attribute value (unchanged)
 +        // ----------------------------------
 +        StatusMod statusMod = new StatusMod();
 +        statusMod.setKey(userTO.getKey());
 +        statusMod.setType(StatusMod.ModType.SUSPEND);
 +        userTO = userService.status(statusMod).readEntity(UserTO.class);
 +        assertEquals("suspended", userTO.getStatus());
 +
 +        connObjectTO = resourceService.readConnObject(RESOURCE_NAME_WS2, AnyTypeKind.USER.name(), userTO.getKey());
 +        assertNotNull(connObjectTO);
 +        assertFalse(connObjectTO.getPlainAttrMap().get("NAME").getValues().isEmpty());
 +        assertEquals("virtualvalue2", connObjectTO.getPlainAttrMap().get("NAME").getValues().get(0));
 +
 +        statusMod = new StatusMod();
 +        statusMod.setKey(userTO.getKey());
 +        statusMod.setType(StatusMod.ModType.REACTIVATE);
 +        userTO = userService.status(statusMod).readEntity(UserTO.class);
 +        assertEquals("active", userTO.getStatus());
 +
 +        connObjectTO = resourceService.readConnObject(RESOURCE_NAME_WS2, AnyTypeKind.USER.name(), userTO.getKey());
 +        assertNotNull(connObjectTO);
 +        assertFalse(connObjectTO.getPlainAttrMap().get("NAME").getValues().isEmpty());
 +        assertEquals("virtualvalue2", connObjectTO.getPlainAttrMap().get("NAME").getValues().get(0));
 +        // ----------------------------------
 +
 +        // ----------------------------------
 +        // update user attribute and check virtual attribute value (unchanged)
 +        // ----------------------------------
 +        userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +
 +        attrMod = new AttrMod();
 +        attrMod.setSchema("surname");
 +        attrMod.getValuesToBeRemoved().add("Surname");
 +        attrMod.getValuesToBeAdded().add("Surname2");
 +
 +        userMod.getPlainAttrsToUpdate().add(attrMod);
 +
 +        userTO = updateUser(userMod);
 +        assertNotNull(userTO);
 +        assertFalse(userTO.getPropagationStatusTOs().isEmpty());
 +        assertEquals(RESOURCE_NAME_WS2, userTO.getPropagationStatusTOs().get(0).getResource());
 +        assertEquals(PropagationTaskExecStatus.SUBMITTED, userTO.getPropagationStatusTOs().get(0).getStatus());
 +
 +        connObjectTO = resourceService.readConnObject(RESOURCE_NAME_WS2, AnyTypeKind.USER.name(), userTO.getKey());
 +        assertNotNull(connObjectTO);
 +        assertEquals("Surname2", connObjectTO.getPlainAttrMap().get("SURNAME").getValues().get(0));
 +
 +        // attribute "name" mapped on virtual attribute "virtualdata" shouldn't be changed
 +        assertFalse(connObjectTO.getPlainAttrMap().get("NAME").getValues().isEmpty());
 +        assertEquals("virtualvalue2", connObjectTO.getPlainAttrMap().get("NAME").getValues().get(0));
 +        // ----------------------------------
 +
 +        // ----------------------------------
 +        // remove user virtual attribute and check virtual attribute value (reset)
 +        // ----------------------------------
 +        userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +        userMod.getVirAttrsToRemove().add("virtualdata");
 +
 +        userTO = updateUser(userMod);
 +        assertNotNull(userTO);
 +        assertTrue(userTO.getVirAttrs().isEmpty());
 +        assertFalse(userTO.getPropagationStatusTOs().isEmpty());
 +        assertEquals(RESOURCE_NAME_WS2, userTO.getPropagationStatusTOs().get(0).getResource());
 +        assertEquals(PropagationTaskExecStatus.SUBMITTED, userTO.getPropagationStatusTOs().get(0).getStatus());
 +
 +        connObjectTO = resourceService.readConnObject(RESOURCE_NAME_WS2, AnyTypeKind.USER.name(), userTO.getKey());
 +        assertNotNull(connObjectTO);
 +
 +        // attribute "name" mapped on virtual attribute "virtualdata" should be reset
 +        assertTrue(connObjectTO.getPlainAttrMap().get("NAME").getValues() == null
 +                || connObjectTO.getPlainAttrMap().get("NAME").getValues().isEmpty());
 +        // ----------------------------------
 +    }
 +
 +    @Test
 +    public void virAttrCache() {
 +        UserTO userTO = UserITCase.getUniqueSampleTO("virattrcache@apache.org");
 +        userTO.getVirAttrs().clear();
 +
 +        AttrTO virAttrTO = new AttrTO();
 +        virAttrTO.setSchema("virtualdata");
 +        virAttrTO.getValues().add("virattrcache");
 +        userTO.getVirAttrs().add(virAttrTO);
 +
 +        userTO.getMemberships().clear();
 +        userTO.getResources().clear();
 +        userTO.getResources().add(RESOURCE_NAME_DBVIRATTR);
 +
 +        // 1. create user
 +        UserTO actual = createUser(userTO);
 +        assertNotNull(actual);
 +
 +        // 2. check for virtual attribute value
 +        actual = userService.read(actual.getKey());
 +        assertEquals("virattrcache", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
 +
 +        // 3. update virtual attribute directly
 +        JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
 +
 +        String value = jdbcTemplate.queryForObject(
 +                "SELECT USERNAME FROM testsync WHERE ID=?", String.class, actual.getKey());
 +        assertEquals("virattrcache", value);
 +
 +        jdbcTemplate.update("UPDATE testsync set USERNAME='virattrcache2' WHERE ID=?", actual.getKey());
 +
 +        value = jdbcTemplate.queryForObject(
 +                "SELECT USERNAME FROM testsync WHERE ID=?", String.class, actual.getKey());
 +        assertEquals("virattrcache2", value);
 +
 +        // 4. check for cached attribute value
 +        actual = userService.read(actual.getKey());
 +        assertEquals("virattrcache", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
 +
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(actual.getKey());
 +
 +        AttrMod virtualdata = new AttrMod();
 +        virtualdata.setSchema("virtualdata");
 +        virtualdata.getValuesToBeAdded().add("virtualupdated");
 +
 +        userMod.getVirAttrsToRemove().add("virtualdata");
 +        userMod.getVirAttrsToUpdate().add(virtualdata);
 +
 +        // 5. update virtual attribute
 +        actual = updateUser(userMod);
 +        assertNotNull(actual);
 +
 +        // 6. check for virtual attribute value
 +        actual = userService.read(actual.getKey());
 +        assertNotNull(actual);
 +        assertEquals("virtualupdated", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE397() {
 +        ResourceTO csv = resourceService.read(RESOURCE_NAME_CSV);
 +        MappingTO origMapping = SerializationUtils.clone(csv.getProvisions().get(0).getMapping());
 +        try {
 +            // change mapping of resource-csv
 +            assertNotNull(origMapping);
 +            for (MappingItemTO item : csv.getProvisions().get(0).getMapping().getItems()) {
 +                if ("email".equals(item.getIntAttrName())) {
 +                    // unset internal attribute mail and set virtual attribute virtualdata as mapped to external email
 +                    item.setIntMappingType(IntMappingType.UserVirtualSchema);
 +                    item.setIntAttrName("virtualdata");
 +                    item.setPurpose(MappingPurpose.BOTH);
 +                    item.setExtAttrName("email");
 +                }
 +            }
 +
 +            resourceService.update(csv);
 +            csv = resourceService.read(RESOURCE_NAME_CSV);
 +            assertNotNull(csv.getProvisions().get(0).getMapping());
 +
 +            boolean found = false;
 +            for (MappingItemTO item : csv.getProvisions().get(0).getMapping().getItems()) {
 +                if ("email".equals(item.getExtAttrName()) && "virtualdata".equals(item.getIntAttrName())) {
 +                    found = true;
 +                }
 +            }
 +
 +            assertTrue(found);
 +
 +            // create a new user
 +            UserTO userTO = UserITCase.getUniqueSampleTO("syncope397@syncope.apache.org");
 +            userTO.getAuxClasses().add("csv");
 +            userTO.getResources().clear();
 +            userTO.getMemberships().clear();
 +            userTO.getDerAttrs().clear();
 +            userTO.getVirAttrs().clear();
 +
 +            userTO.getDerAttrs().add(attrTO("csvuserid", null));
 +            userTO.getDerAttrs().add(attrTO("cn", null));
 +            userTO.getVirAttrs().add(attrTO("virtualdata", "test@testone.org"));
 +            // assign resource-csv to user
 +            userTO.getResources().add(RESOURCE_NAME_CSV);
 +            // save user
 +            UserTO created = createUser(userTO);
 +            // make std controls about user
 +            assertNotNull(created);
 +            assertTrue(RESOURCE_NAME_CSV.equals(created.getResources().iterator().next()));
 +            // update user
 +            UserTO toBeUpdated = userService.read(created.getKey());
 +            UserMod userMod = new UserMod();
 +            userMod.setKey(toBeUpdated.getKey());
 +            userMod.setPassword("password234");
 +            // assign new resource to user
 +            userMod.getResourcesToAdd().add(RESOURCE_NAME_WS2);
 +            //modify virtual attribute
 +            userMod.getVirAttrsToRemove().add("virtualdata");
 +            userMod.getVirAttrsToUpdate().add(attrMod("virtualdata", "test@testoneone.com"));
 +
 +            // check Syncope change password
 +            StatusMod pwdPropRequest = new StatusMod();
 +            pwdPropRequest.setOnSyncope(true);
-             pwdPropRequest.getResourceNames().add(RESOURCE_NAME_WS2);
++            pwdPropRequest.getResources().add(RESOURCE_NAME_WS2);
 +            userMod.setPwdPropRequest(pwdPropRequest);
 +
 +            toBeUpdated = updateUser(userMod);
 +            assertNotNull(toBeUpdated);
 +            assertEquals("test@testoneone.com", toBeUpdated.getVirAttrs().iterator().next().getValues().get(0));
 +            // check if propagates correctly with assertEquals on size of tasks list
 +            assertEquals(2, toBeUpdated.getPropagationStatusTOs().size());
 +        } finally {
 +            // restore mapping of resource-csv
 +            csv.getProvisions().get(0).setMapping(origMapping);
 +            resourceService.update(csv);
 +        }
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE442() {
 +        UserTO userTO = UserITCase.getUniqueSampleTO("syncope442@apache.org");
 +        userTO.getVirAttrs().clear();
 +
 +        AttrTO virAttrTO = new AttrTO();
 +        virAttrTO.setSchema("virtualdata");
 +        virAttrTO.getValues().add("virattrcache");
 +        userTO.getVirAttrs().add(virAttrTO);
 +
 +        userTO.getMemberships().clear();
 +        userTO.getResources().clear();
 +        userTO.getResources().add(RESOURCE_NAME_DBVIRATTR);
 +
 +        // 1. create user
 +        UserTO actual = createUser(userTO);
 +        assertNotNull(actual);
 +
 +        // 2. check for virtual attribute value
 +        actual = userService.read(actual.getKey());
 +        assertEquals("virattrcache", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
 +
 +        // ----------------------------------------
 +        // 3. force cache expiring without any modification
 +        // ----------------------------------------
 +        String jdbcURL = null;
 +        ConnInstanceTO connInstanceTO = connectorService.readByResource(
 +                RESOURCE_NAME_DBVIRATTR, Locale.ENGLISH.getLanguage());
 +        for (ConnConfProperty prop : connInstanceTO.getConfiguration()) {
 +            if ("jdbcUrlTemplate".equals(prop.getSchema().getName())) {
 +                jdbcURL = prop.getValues().iterator().next().toString();
 +                prop.getValues().clear();
 +                prop.getValues().add("jdbc:h2:tcp://localhost:9092/xxx");
 +            }
 +        }
 +
 +        connectorService.update(connInstanceTO);
 +
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(actual.getKey());
 +
 +        AttrMod virtualdata = new AttrMod();
 +        virtualdata.setSchema("virtualdata");
 +        virtualdata.getValuesToBeAdded().add("virtualupdated");
 +
 +        userMod.getVirAttrsToRemove().add("virtualdata");
 +        userMod.getVirAttrsToUpdate().add(virtualdata);
 +
 +        actual = updateUser(userMod);
 +        assertNotNull(actual);
 +        // ----------------------------------------
 +
 +        // ----------------------------------------
 +        // 4. update virtual attribute
 +        // ----------------------------------------
 +        final JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
 +
 +        String value = jdbcTemplate.queryForObject(
 +                "SELECT USERNAME FROM testsync WHERE ID=?", String.class, actual.getKey());
 +        assertEquals("virattrcache", value);
 +
 +        jdbcTemplate.update("UPDATE testsync set USERNAME='virattrcache2' WHERE ID=?", actual.getKey());
 +
 +        value = jdbcTemplate.queryForObject(
 +                "SELECT USERNAME FROM testsync WHERE ID=?", String.class, actual.getKey());
 +        assertEquals("virattrcache2", value);
 +        // ----------------------------------------
 +
 +        actual = userService.read(actual.getKey());
 +        assertEquals("virattrcache", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
 +
 +        // ----------------------------------------
 +        // 5. restore connector
 +        // ----------------------------------------
 +        for (ConnConfProperty prop : connInstanceTO.getConfiguration()) {
 +            if ("jdbcUrlTemplate".equals(prop.getSchema().getName())) {
 +                prop.getValues().clear();
 +                prop.getValues().add(jdbcURL);
 +            }
 +        }
 +
 +        connectorService.update(connInstanceTO);
 +        // ----------------------------------------
 +
 +        actual = userService.read(actual.getKey());
 +        assertEquals("virattrcache2", actual.getVirAttrMap().get("virtualdata").getValues().get(0));
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE436() {
 +        UserTO userTO = UserITCase.getUniqueSampleTO("syncope436@syncope.apache.org");
 +        userTO.getMemberships().clear();
 +        userTO.getResources().clear();
 +        userTO.getResources().add(RESOURCE_NAME_LDAP);
 +        userTO.getVirAttrs().add(attrTO("virtualReadOnly", "readOnly"));
 +        userTO = createUser(userTO);
 +        //Finding no values because the virtual attribute is readonly 
 +        assertTrue(userTO.getVirAttrMap().get("virtualReadOnly").getValues().isEmpty());
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE453() {
 +        final String resourceName = "issueSYNCOPE453-Res-" + getUUIDString();
 +        final String groupName = "issueSYNCOPE453-Group-" + getUUIDString();
 +
 +        // -------------------------------------------
 +        // Create a resource ad-hoc
 +        // -------------------------------------------
 +        ResourceTO resourceTO = new ResourceTO();
 +
 +        resourceTO.setKey(resourceName);
 +        resourceTO.setConnector(107L);
 +
 +        ProvisionTO provisionTO = new ProvisionTO();
 +        provisionTO.setAnyType(AnyTypeKind.USER.name());
 +        provisionTO.setObjectClass(ObjectClass.ACCOUNT_NAME);
 +        resourceTO.getProvisions().add(provisionTO);
 +
 +        MappingTO mapping = new MappingTO();
 +        provisionTO.setMapping(mapping);
 +
 +        MappingItemTO item = new MappingItemTO();
 +        item.setIntAttrName("aLong");
 +        item.setIntMappingType(IntMappingType.UserPlainSchema);
 +        item.setExtAttrName("ID");
 +        item.setPurpose(MappingPurpose.PROPAGATION);
 +        item.setConnObjectKey(true);
 +        mapping.setConnObjectKeyItem(item);
 +
 +        item = new MappingItemTO();
 +        item.setExtAttrName("USERNAME");
 +        item.setIntAttrName("username");
 +        item.setIntMappingType(IntMappingType.Username);
 +        item.setPurpose(MappingPurpose.PROPAGATION);
 +        mapping.getItems().add(item);
 +
 +        item = new MappingItemTO();
 +        item.setExtAttrName("EMAIL");
 +        item.setIntAttrName("rvirtualdata");
 +        item.setIntMappingType(IntMappingType.GroupVirtualSchema);
 +        item.setPurpose(MappingPurpose.PROPAGATION);
 +        mapping.getItems().add(item);
 +
 +        assertNotNull(getObject(
 +                resourceService.create(resourceTO).getLocation(), ResourceService.class, ResourceTO.class));
 +        // -------------------------------------------
 +
 +        // -------------------------------------------
 +        // Create a VirAttrITCase ad-hoc
 +        // -------------------------------------------
 +        GroupTO groupTO = new GroupTO();
 +        groupTO.setName(groupName);
 +        groupTO.setRealm("/");
 +        groupTO.getVirAttrs().add(attrTO("rvirtualdata", "ml@group.it"));
 +        groupTO.getResources().add(RESOURCE_NAME_LDAP);
 +        groupTO = createGroup(groupTO);
 +        assertEquals(1, groupTO.getVirAttrs().size());
 +        assertEquals("ml@group.it", groupTO.getVirAttrs().iterator().next().getValues().get(0));
 +        // -------------------------------------------
 +
 +        // -------------------------------------------
 +        // Create new user
 +        // -------------------------------------------
 +        UserTO userTO = UserITCase.getUniqueSampleTO("syncope453@syncope.apache.org");
 +        userTO.getPlainAttrs().add(attrTO("aLong", "123"));
 +        userTO.getResources().clear();
 +        userTO.getResources().add(resourceName);
 +        userTO.getVirAttrs().clear();
 +        userTO.getDerAttrs().clear();
 +        userTO.getMemberships().clear();
 +
 +        MembershipTO membership = new MembershipTO();
 +        membership.setRightKey(groupTO.getKey());
 +        userTO.getMemberships().add(membership);
 +
 +        userTO = createUser(userTO);
 +        assertEquals(2, userTO.getPropagationStatusTOs().size());
 +        assertTrue(userTO.getPropagationStatusTOs().get(0).getStatus().isSuccessful());
 +        assertTrue(userTO.getPropagationStatusTOs().get(1).getStatus().isSuccessful());
 +
 +        JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
 +
 +        final Map<String, Object> actuals = jdbcTemplate.queryForMap(
 +                "SELECT id, surname, email FROM testsync WHERE id=?",
 +                new Object[] { Integer.parseInt(userTO.getPlainAttrMap().get("aLong").getValues().get(0)) });
 +
 +        assertEquals(userTO.getPlainAttrMap().get("aLong").getValues().get(0), actuals.get("id").toString());
 +        assertEquals("ml@group.it", actuals.get("email"));
 +        // -------------------------------------------
 +
 +        // -------------------------------------------
 +        // Delete resource and group ad-hoc
 +        // -------------------------------------------
 +        resourceService.delete(resourceName);
 +        groupService.delete(groupTO.getKey());
 +        // -------------------------------------------
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE459() {
 +        UserTO userTO = UserITCase.getUniqueSampleTO("syncope459@apache.org");
 +        userTO.getResources().clear();
 +        userTO.getMemberships().clear();
 +        userTO.getVirAttrs().clear();
 +
 +        AttrTO virtualReadOnly = attrTO("virtualReadOnly", "");
 +        virtualReadOnly.getValues().clear();
 +
 +        userTO.getVirAttrs().add(virtualReadOnly);
 +
 +        userTO = createUser(userTO);
 +
 +        assertNotNull(userTO.getVirAttrMap().get("virtualReadOnly"));
 +
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +
 +        AttrMod virtualdata = new AttrMod();
 +        virtualdata.setSchema("virtualdata");
 +
 +        userMod.getVirAttrsToUpdate().add(virtualdata);
 +
 +        userTO = updateUser(userMod);
 +        assertNotNull(userTO.getVirAttrMap().get("virtualdata"));
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE501() {
 +        // 1. create user and propagate him on resource-db-virattr
 +        UserTO userTO = UserITCase.getUniqueSampleTO("syncope501@apache.org");
 +        userTO.getResources().clear();
 +        userTO.getMemberships().clear();
 +        userTO.getVirAttrs().clear();
 +
 +        userTO.getResources().add(RESOURCE_NAME_DBVIRATTR);
 +
 +        // virtualdata is mapped with username
 +        final AttrTO virtualData = attrTO("virtualdata", "syncope501@apache.org");
 +        userTO.getVirAttrs().add(virtualData);
 +
 +        userTO = createUser(userTO);
 +
 +        assertNotNull(userTO.getVirAttrMap().get("virtualdata"));
 +        assertEquals("syncope501@apache.org", userTO.getVirAttrMap().get("virtualdata").getValues().get(0));
 +
 +        // 2. update virtual attribute
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +
 +        final StatusMod statusMod = new StatusMod();
-         statusMod.getResourceNames().addAll(Collections.<String>emptySet());
++        statusMod.getResources().addAll(Collections.<String>emptySet());
 +        statusMod.setOnSyncope(false);
 +
 +        userMod.setPwdPropRequest(statusMod);
 +        // change virtual attribute value
 +        final AttrMod virtualDataMod = new AttrMod();
 +        virtualDataMod.setSchema("virtualdata");
 +        virtualDataMod.getValuesToBeAdded().add("syncope501_updated@apache.org");
 +        virtualDataMod.getValuesToBeRemoved().add("syncope501@apache.org");
 +        userMod.getVirAttrsToUpdate().add(virtualDataMod);
 +        userMod.getVirAttrsToRemove().add("virtualdata");
 +
 +        userTO = updateUser(userMod);
 +        assertNotNull(userTO);
 +
 +        // 3. check that user virtual attribute has really been updated 
 +        assertFalse(userTO.getVirAttrMap().get("virtualdata").getValues().isEmpty());
 +        assertEquals("syncope501_updated@apache.org", userTO.getVirAttrMap().get("virtualdata").getValues().get(0));
 +    }
 +}


[32/33] syncope git commit: Merge branch '1_2_X'

Posted by md...@apache.org.
Merge branch '1_2_X'

Conflicts:
	pom.xml


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

Branch: refs/heads/SYNCOPE-156
Commit: e54592850c95cde8d26f2182f41fe5ce21398512
Parents: 4412ac7 39eb575
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Wed Aug 26 17:39:53 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Wed Aug 26 17:39:53 2015 +0200

----------------------------------------------------------------------

----------------------------------------------------------------------



[12/33] syncope git commit: Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/syncope

Posted by md...@apache.org.
Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/syncope


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

Branch: refs/heads/SYNCOPE-156
Commit: 77f697b2dbb05c81818288f74f3f7b7656db8b7f
Parents: 8e21972 4cdb34b
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Thu Aug 20 11:41:11 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Thu Aug 20 11:41:11 2015 +0200

----------------------------------------------------------------------
 .../lib/policy/AbstractAccountRuleConf.java     |  52 +++
 .../lib/policy/AbstractPasswordRuleConf.java    |  52 +++
 .../common/lib/policy/AbstractPolicyTO.java     |  97 ++++++
 .../common/lib/policy/AccountPolicyTO.java      |  77 +++++
 .../common/lib/policy/AccountRuleConf.java      |  23 ++
 .../lib/policy/DefaultAccountRuleConf.java      |  94 ++++++
 .../lib/policy/DefaultPasswordRuleConf.java     | 277 ++++++++++++++++
 .../common/lib/policy/PasswordPolicyTO.java     |  68 ++++
 .../common/lib/policy/PasswordRuleConf.java     |  23 ++
 .../syncope/common/lib/policy/PolicyType.java   |  43 +++
 .../common/lib/policy/PushPolicySpec.java       |  28 ++
 .../syncope/common/lib/policy/RuleConf.java     |  32 ++
 .../common/lib/policy/SyncPolicySpec.java       |  63 ++++
 .../syncope/common/lib/policy/SyncPolicyTO.java |  45 +++
 .../common/lib/wrap/ReportletConfClass.java     |  30 --
 .../misc/policy/InvalidPasswordRuleConf.java    |  37 +++
 .../misc/security/DefaultPasswordGenerator.java | 330 +++++++++++++++++++
 .../core/persistence/api/dao/AccountRule.java   |  27 ++
 .../api/dao/AccountRuleConfClass.java           |  32 ++
 .../core/persistence/api/dao/PasswordRule.java  |  27 ++
 .../api/dao/PasswordRuleConfClass.java          |  32 ++
 .../api/entity/policy/AccountPolicy.java        |  50 +++
 .../api/entity/policy/PasswordPolicy.java       |  40 +++
 .../api/entity/policy/PushPolicy.java           |  29 ++
 .../api/entity/policy/SyncPolicy.java           |  30 ++
 .../persistence/jpa/dao/DefaultAccountRule.java | 112 +++++++
 .../jpa/dao/DefaultPasswordRule.java            | 218 ++++++++++++
 .../jpa/entity/policy/AbstractPolicy.java       |  67 ++++
 .../jpa/entity/policy/JPAAccountPolicy.java     | 160 +++++++++
 .../policy/JPAAccountRuleConfInstance.java      |  73 ++++
 .../jpa/entity/policy/JPAPasswordPolicy.java    | 115 +++++++
 .../policy/JPAPasswordRuleConfInstance.java     |  73 ++++
 .../jpa/entity/policy/JPAPushPolicy.java        |  55 ++++
 .../jpa/entity/policy/JPASyncPolicy.java        |  54 +++
 .../jpa/entity/task/AbstractTask.java           |  96 ++++++
 .../fit/core/reference/TestAccountRule.java     |  48 +++
 .../fit/core/reference/TestAccountRuleConf.java |  36 ++
 .../fit/core/reference/TestPasswordRule.java    |  48 +++
 .../core/reference/TestPasswordRuleConf.java    |  36 ++
 39 files changed, 2799 insertions(+), 30 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractAccountRuleConf.java
----------------------------------------------------------------------
diff --cc common/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractAccountRuleConf.java
index 0000000,0000000..8fe5e24
new file mode 100644
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractAccountRuleConf.java
@@@ -1,0 -1,0 +1,52 @@@
++/*
++ * 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.common.lib.report;
++
++import javax.xml.bind.annotation.XmlSeeAlso;
++import javax.xml.bind.annotation.XmlType;
++import org.apache.commons.lang3.StringUtils;
++import org.apache.syncope.common.lib.AbstractBaseBean;
++
++@XmlType
++@XmlSeeAlso({ StaticReportletConf.class, UserReportletConf.class, GroupReportletConf.class })
++public abstract class AbstractReportletConf extends AbstractBaseBean implements ReportletConf {
++
++    private static final long serialVersionUID = -6130008602014516608L;
++
++    private String name;
++
++    public AbstractReportletConf() {
++        this(StringUtils.EMPTY);
++        setName(getClass().getName());
++    }
++
++    public AbstractReportletConf(final String name) {
++        super();
++        this.name = name;
++    }
++
++    @Override
++    public final String getName() {
++        return name;
++    }
++
++    public final void setName(final String name) {
++        this.name = name;
++    }
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractPasswordRuleConf.java
----------------------------------------------------------------------
diff --cc common/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractPasswordRuleConf.java
index 0000000,0000000..8fe5e24
new file mode 100644
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractPasswordRuleConf.java
@@@ -1,0 -1,0 +1,52 @@@
++/*
++ * 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.common.lib.report;
++
++import javax.xml.bind.annotation.XmlSeeAlso;
++import javax.xml.bind.annotation.XmlType;
++import org.apache.commons.lang3.StringUtils;
++import org.apache.syncope.common.lib.AbstractBaseBean;
++
++@XmlType
++@XmlSeeAlso({ StaticReportletConf.class, UserReportletConf.class, GroupReportletConf.class })
++public abstract class AbstractReportletConf extends AbstractBaseBean implements ReportletConf {
++
++    private static final long serialVersionUID = -6130008602014516608L;
++
++    private String name;
++
++    public AbstractReportletConf() {
++        this(StringUtils.EMPTY);
++        setName(getClass().getName());
++    }
++
++    public AbstractReportletConf(final String name) {
++        super();
++        this.name = name;
++    }
++
++    @Override
++    public final String getName() {
++        return name;
++    }
++
++    public final void setName(final String name) {
++        this.name = name;
++    }
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractPolicyTO.java
----------------------------------------------------------------------
diff --cc common/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractPolicyTO.java
index 0000000,0000000..9157ba2
new file mode 100644
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractPolicyTO.java
@@@ -1,0 -1,0 +1,97 @@@
++/*
++ * 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.common.lib.policy;
++
++import com.fasterxml.jackson.annotation.JsonProperty;
++import com.fasterxml.jackson.annotation.JsonTypeInfo;
++import java.util.ArrayList;
++import java.util.List;
++import javax.ws.rs.PathParam;
++import javax.xml.bind.annotation.XmlElement;
++import javax.xml.bind.annotation.XmlElementWrapper;
++import javax.xml.bind.annotation.XmlRootElement;
++import javax.xml.bind.annotation.XmlSeeAlso;
++import javax.xml.bind.annotation.XmlType;
++import org.apache.syncope.common.lib.AbstractBaseBean;
++import org.apache.syncope.common.lib.types.PolicyType;
++
++@XmlRootElement(name = "abstractPolicy")
++@XmlType
++@XmlSeeAlso({ AccountPolicyTO.class, PasswordPolicyTO.class, SyncPolicyTO.class })
++@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
++public abstract class AbstractPolicyTO extends AbstractBaseBean {
++
++    private static final long serialVersionUID = -2903888572649721035L;
++
++    private long key;
++
++    private String description;
++
++    private final PolicyType type;
++
++    private final List<String> usedByResources = new ArrayList<>();
++
++    private final List<String> usedByRealms = new ArrayList<>();
++
++    private AbstractPolicyTO() {
++        super();
++        throw new UnsupportedOperationException("No-arg constructor is just to keep JAXB from complaining");
++    }
++
++    protected AbstractPolicyTO(final PolicyType type) {
++        super();
++        this.type = type;
++    }
++
++    public long getKey() {
++        return key;
++    }
++
++    @PathParam("key")
++    public void setKey(final long key) {
++        this.key = key;
++    }
++
++    public String getDescription() {
++        return description;
++    }
++
++    public void setDescription(final String description) {
++        this.description = description;
++    }
++
++    public PolicyType getType() {
++        return type;
++    }
++
++    @XmlElementWrapper(name = "usedByResources")
++    @XmlElement(name = "resource")
++    @JsonProperty("usedByResources")
++    public List<String> getUsedByResources() {
++        return usedByResources;
++    }
++
++    @XmlElementWrapper(name = "usedByRealms")
++    @XmlElement(name = "group")
++    @JsonProperty("usedByRealms")
++    public List<String> getUsedByRealms() {
++        return usedByRealms;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AccountPolicyTO.java
----------------------------------------------------------------------
diff --cc common/lib/src/main/java/org/apache/syncope/common/lib/policy/AccountPolicyTO.java
index 0000000,0000000..67d9fe0
new file mode 100644
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AccountPolicyTO.java
@@@ -1,0 -1,0 +1,77 @@@
++/*
++ * 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.common.lib.policy;
++
++import com.fasterxml.jackson.annotation.JsonProperty;
++import java.util.ArrayList;
++import java.util.List;
++import javax.xml.bind.annotation.XmlElement;
++import javax.xml.bind.annotation.XmlElementWrapper;
++import javax.xml.bind.annotation.XmlRootElement;
++import javax.xml.bind.annotation.XmlType;
++import org.apache.syncope.common.lib.types.PolicyType;
++
++@XmlRootElement(name = "accountPolicy")
++@XmlType
++public class AccountPolicyTO extends AbstractPolicyTO {
++
++    private static final long serialVersionUID = -1557150042828800134L;
++
++    private boolean propagateSuspension;
++
++    private int maxAuthenticationAttempts;
++
++    private final List<AccountRuleConf> ruleConfs = new ArrayList<>();
++
++    private final List<String> resources = new ArrayList<>();
++
++    public AccountPolicyTO() {
++        super(PolicyType.ACCOUNT);
++    }
++
++    public boolean isPropagateSuspension() {
++        return propagateSuspension;
++    }
++
++    public void setPropagateSuspension(final boolean propagateSuspension) {
++        this.propagateSuspension = propagateSuspension;
++    }
++
++    public int getMaxAuthenticationAttempts() {
++        return maxAuthenticationAttempts;
++    }
++
++    public void setMaxAuthenticationAttempts(final int maxAuthenticationAttempts) {
++        this.maxAuthenticationAttempts = maxAuthenticationAttempts;
++    }
++
++    @XmlElementWrapper(name = "ruleConfs")
++    @XmlElement(name = "ruleConf")
++    @JsonProperty("ruleConfs")
++    public List<AccountRuleConf> getRuleConfs() {
++        return ruleConfs;
++    }
++
++    @XmlElementWrapper(name = "resources")
++    @XmlElement(name = "resource")
++    @JsonProperty("resources")
++    public List<String> getResources() {
++        return resources;
++    }
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AccountRuleConf.java
----------------------------------------------------------------------
diff --cc common/lib/src/main/java/org/apache/syncope/common/lib/policy/AccountRuleConf.java
index 0000000,0000000..aa107fc
new file mode 100644
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AccountRuleConf.java
@@@ -1,0 -1,0 +1,23 @@@
++/*
++ * 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.common.lib.policy;
++
++public interface AccountRuleConf extends RuleConf {
++
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/common/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultAccountRuleConf.java
----------------------------------------------------------------------
diff --cc common/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultAccountRuleConf.java
index 0000000,0000000..929f05b
new file mode 100644
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultAccountRuleConf.java
@@@ -1,0 -1,0 +1,94 @@@
++/*
++ * 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.common.lib.policy;
++
++import javax.xml.bind.annotation.XmlRootElement;
++import javax.xml.bind.annotation.XmlType;
++
++@XmlRootElement(name = "defaultAccountRuleConf")
++@XmlType
++public class DefaultAccountRuleConf extends AbstractRuleConf implements AccountRuleConf {
++
++    private static final long serialVersionUID = 3259256974414758406L;
++
++    /**
++     * Minimum length.
++     */
++    private int maxLength;
++
++    /**
++     * Maximum length.
++     */
++    private int minLength;
++
++    /**
++     * Pattern (regular expression) that must match.
++     */
++    private String pattern;
++
++    /**
++     * Specify if one or more lowercase characters are permitted.
++     */
++    private boolean allUpperCase;
++
++    /**
++     * Specify if one or more uppercase characters are permitted.
++     */
++    private boolean allLowerCase;
++
++    public boolean isAllLowerCase() {
++        return allLowerCase;
++    }
++
++    public void setAllLowerCase(final boolean allLowerCase) {
++        this.allLowerCase = allLowerCase;
++    }
++
++    public boolean isAllUpperCase() {
++        return allUpperCase;
++    }
++
++    public void setAllUpperCase(final boolean allUpperCase) {
++        this.allUpperCase = allUpperCase;
++    }
++
++    public int getMaxLength() {
++        return maxLength;
++    }
++
++    public void setMaxLength(final int maxLength) {
++        this.maxLength = maxLength;
++    }
++
++    public int getMinLength() {
++        return minLength;
++    }
++
++    public void setMinLength(final int minLength) {
++        this.minLength = minLength;
++    }
++
++    public String getPattern() {
++        return pattern;
++    }
++
++    public void setPattern(final String pattern) {
++        this.pattern = pattern;
++    }
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/common/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultPasswordRuleConf.java
----------------------------------------------------------------------
diff --cc common/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultPasswordRuleConf.java
index 0000000,0000000..629e820
new file mode 100644
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultPasswordRuleConf.java
@@@ -1,0 -1,0 +1,277 @@@
++/*
++ * 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.common.lib.policy;
++
++import javax.xml.bind.annotation.XmlRootElement;
++import javax.xml.bind.annotation.XmlType;
++
++@XmlRootElement(name = "defaultPasswordRuleConf")
++@XmlType
++public class DefaultPasswordRuleConf extends AbstractRuleConf implements PasswordRuleConf {
++
++    private static final long serialVersionUID = -7988778083915548547L;
++
++    /**
++     * Minimum length.
++     */
++    private int maxLength;
++
++    /**
++     * Maximum length.
++     */
++    private int minLength;
++
++    /**
++     * Specify if one or more non alphanumeric characters are required.
++     */
++    private boolean nonAlphanumericRequired;
++
++    /**
++     * Specify if one or more alphanumeric characters are required.
++     */
++    private boolean alphanumericRequired;
++
++    /**
++     * Specify if one or more digits are required.
++     */
++    private boolean digitRequired;
++
++    /**
++     * Specify if one or more lowercase alphabetic characters are required.
++     */
++    private boolean lowercaseRequired;
++
++    /**
++     * Specify if one or more uppercase alphabetic characters are required.
++     */
++    private boolean uppercaseRequired;
++
++    /**
++     * Specify if must start with a digit.
++     */
++    private boolean mustStartWithDigit;
++
++    /**
++     * Specify if mustn't start with a digit.
++     */
++    private boolean mustntStartWithDigit;
++
++    /**
++     * Specify if must end with a digit.
++     */
++    private boolean mustEndWithDigit;
++
++    /**
++     * Specify if mustn't end with a digit.
++     */
++    private boolean mustntEndWithDigit;
++
++    /**
++     * Specify if must start with a non alphanumeric character.
++     */
++    private boolean mustStartWithNonAlpha;
++
++    /**
++     * Specify if must start with a alphanumeric character.
++     */
++    private boolean mustStartWithAlpha;
++
++    /**
++     * Specify if mustn't start with a non alphanumeric character.
++     */
++    private boolean mustntStartWithNonAlpha;
++
++    /**
++     * Specify if mustn't start with a alphanumeric character.
++     */
++    private boolean mustntStartWithAlpha;
++
++    /**
++     * Specify if must end with a non alphanumeric character.
++     */
++    private boolean mustEndWithNonAlpha;
++
++    /**
++     * Specify if must end with a alphanumeric character.
++     */
++    private boolean mustEndWithAlpha;
++
++    /**
++     * Specify if mustn't end with a non alphanumeric character.
++     */
++    private boolean mustntEndWithNonAlpha;
++
++    /**
++     * Specify if mustn't end with a alphanumeric character.
++     */
++    private boolean mustntEndWithAlpha;
++
++    public boolean isDigitRequired() {
++        return digitRequired;
++    }
++
++    public void setDigitRequired(final boolean digitRequired) {
++        this.digitRequired = digitRequired;
++    }
++
++    public boolean isLowercaseRequired() {
++        return lowercaseRequired;
++    }
++
++    public void setLowercaseRequired(final boolean lowercaseRequired) {
++        this.lowercaseRequired = lowercaseRequired;
++    }
++
++    public int getMaxLength() {
++        return maxLength;
++    }
++
++    public void setMaxLength(final int maxLength) {
++        this.maxLength = maxLength;
++    }
++
++    public int getMinLength() {
++        return minLength;
++    }
++
++    public void setMinLength(final int minLength) {
++        this.minLength = minLength;
++    }
++
++    public boolean isMustEndWithDigit() {
++        return mustEndWithDigit;
++    }
++
++    public void setMustEndWithDigit(final boolean mustEndWithDigit) {
++        this.mustEndWithDigit = mustEndWithDigit;
++    }
++
++    public boolean isMustEndWithNonAlpha() {
++        return mustEndWithNonAlpha;
++    }
++
++    public void setMustEndWithNonAlpha(final boolean mustEndWithNonAlpha) {
++        this.mustEndWithNonAlpha = mustEndWithNonAlpha;
++    }
++
++    public boolean isMustStartWithDigit() {
++        return mustStartWithDigit;
++    }
++
++    public void setMustStartWithDigit(final boolean mustStartWithDigit) {
++        this.mustStartWithDigit = mustStartWithDigit;
++    }
++
++    public boolean isMustStartWithNonAlpha() {
++        return mustStartWithNonAlpha;
++    }
++
++    public void setMustStartWithNonAlpha(final boolean mustStartWithNonAlpha) {
++        this.mustStartWithNonAlpha = mustStartWithNonAlpha;
++    }
++
++    public boolean isMustntEndWithDigit() {
++        return mustntEndWithDigit;
++    }
++
++    public void setMustntEndWithDigit(final boolean mustntEndWithDigit) {
++        this.mustntEndWithDigit = mustntEndWithDigit;
++    }
++
++    public boolean isMustntEndWithNonAlpha() {
++        return mustntEndWithNonAlpha;
++    }
++
++    public void setMustntEndWithNonAlpha(final boolean mustntEndWithNonAlpha) {
++        this.mustntEndWithNonAlpha = mustntEndWithNonAlpha;
++    }
++
++    public boolean isMustntStartWithDigit() {
++        return mustntStartWithDigit;
++    }
++
++    public void setMustntStartWithDigit(final boolean mustntStartWithDigit) {
++        this.mustntStartWithDigit = mustntStartWithDigit;
++    }
++
++    public boolean isMustntStartWithNonAlpha() {
++        return mustntStartWithNonAlpha;
++    }
++
++    public void setMustntStartWithNonAlpha(final boolean mustntStartWithNonAlpha) {
++        this.mustntStartWithNonAlpha = mustntStartWithNonAlpha;
++    }
++
++    public boolean isNonAlphanumericRequired() {
++        return nonAlphanumericRequired;
++    }
++
++    public void setNonAlphanumericRequired(final boolean nonAlphanumericRequired) {
++        this.nonAlphanumericRequired = nonAlphanumericRequired;
++    }
++
++    public boolean isUppercaseRequired() {
++        return uppercaseRequired;
++    }
++
++    public void setUppercaseRequired(final boolean uppercaseRequired) {
++        this.uppercaseRequired = uppercaseRequired;
++    }
++
++    public boolean isAlphanumericRequired() {
++        return alphanumericRequired;
++    }
++
++    public void setAlphanumericRequired(final boolean alphanumericRequired) {
++        this.alphanumericRequired = alphanumericRequired;
++    }
++
++    public boolean isMustEndWithAlpha() {
++        return mustEndWithAlpha;
++    }
++
++    public void setMustEndWithAlpha(final boolean mustEndWithAlpha) {
++        this.mustEndWithAlpha = mustEndWithAlpha;
++    }
++
++    public boolean isMustStartWithAlpha() {
++        return mustStartWithAlpha;
++    }
++
++    public void setMustStartWithAlpha(final boolean mustStartWithAlpha) {
++        this.mustStartWithAlpha = mustStartWithAlpha;
++    }
++
++    public boolean isMustntEndWithAlpha() {
++        return mustntEndWithAlpha;
++    }
++
++    public void setMustntEndWithAlpha(final boolean mustntEndWithAlpha) {
++        this.mustntEndWithAlpha = mustntEndWithAlpha;
++    }
++
++    public boolean isMustntStartWithAlpha() {
++        return mustntStartWithAlpha;
++    }
++
++    public void setMustntStartWithAlpha(final boolean mustntStartWithAlpha) {
++        this.mustntStartWithAlpha = mustntStartWithAlpha;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/common/lib/src/main/java/org/apache/syncope/common/lib/policy/PasswordPolicyTO.java
----------------------------------------------------------------------
diff --cc common/lib/src/main/java/org/apache/syncope/common/lib/policy/PasswordPolicyTO.java
index 0000000,0000000..cf91df2
new file mode 100644
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/PasswordPolicyTO.java
@@@ -1,0 -1,0 +1,68 @@@
++/*
++ * 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.common.lib.policy;
++
++import com.fasterxml.jackson.annotation.JsonProperty;
++import java.util.ArrayList;
++import java.util.List;
++import javax.xml.bind.annotation.XmlElement;
++import javax.xml.bind.annotation.XmlElementWrapper;
++import javax.xml.bind.annotation.XmlRootElement;
++import javax.xml.bind.annotation.XmlType;
++import org.apache.syncope.common.lib.types.PolicyType;
++
++@XmlRootElement(name = "passwordPolicy")
++@XmlType
++public class PasswordPolicyTO extends AbstractPolicyTO {
++
++    private static final long serialVersionUID = -5606086441294799690L;
++
++    private boolean allowNullPassword;
++
++    private int historyLength;
++
++    private final List<PasswordRuleConf> ruleConfs = new ArrayList<>();
++
++    public PasswordPolicyTO() {
++        super(PolicyType.PASSWORD);
++    }
++
++    public boolean isAllowNullPassword() {
++        return allowNullPassword;
++    }
++
++    public void setAllowNullPassword(final boolean allowNullPassword) {
++        this.allowNullPassword = allowNullPassword;
++    }
++
++    public int getHistoryLength() {
++        return historyLength;
++    }
++
++    public void setHistoryLength(final int historyLength) {
++        this.historyLength = historyLength;
++    }
++
++    @XmlElementWrapper(name = "ruleConfs")
++    @XmlElement(name = "ruleConf")
++    @JsonProperty("ruleConfs")
++    public List<PasswordRuleConf> getRuleConfs() {
++        return ruleConfs;
++    }
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/common/lib/src/main/java/org/apache/syncope/common/lib/policy/PasswordRuleConf.java
----------------------------------------------------------------------
diff --cc common/lib/src/main/java/org/apache/syncope/common/lib/policy/PasswordRuleConf.java
index 0000000,0000000..cf71500
new file mode 100644
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/PasswordRuleConf.java
@@@ -1,0 -1,0 +1,23 @@@
++/*
++ * 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.common.lib.policy;
++
++public interface PasswordRuleConf extends RuleConf {
++
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/common/lib/src/main/java/org/apache/syncope/common/lib/policy/PolicyType.java
----------------------------------------------------------------------
diff --cc common/lib/src/main/java/org/apache/syncope/common/lib/policy/PolicyType.java
index 0000000,0000000..7f1c11e
new file mode 100644
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/PolicyType.java
@@@ -1,0 -1,0 +1,43 @@@
++/*
++ * 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.common.lib.types;
++
++import javax.xml.bind.annotation.XmlEnum;
++
++@XmlEnum
++public enum PolicyType {
++
++    /**
++     * How username values should look like.
++     */
++    ACCOUNT,
++    /**
++     * How password values should look like.
++     */
++    PASSWORD,
++    /**
++     * For handling conflicts resolution during synchronization.
++     */
++    SYNC,
++    /**
++     * For handling conflicts resolution during push.
++     */
++    PUSH;
++
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/common/lib/src/main/java/org/apache/syncope/common/lib/policy/PushPolicySpec.java
----------------------------------------------------------------------
diff --cc common/lib/src/main/java/org/apache/syncope/common/lib/policy/PushPolicySpec.java
index 0000000,0000000..4b61374
new file mode 100644
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/PushPolicySpec.java
@@@ -1,0 -1,0 +1,28 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.common.lib.policy;
++
++import javax.xml.bind.annotation.XmlType;
++
++@XmlType
++public class PushPolicySpec {
++
++    private static final long serialVersionUID = 3641030189482617497L;
++
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/common/lib/src/main/java/org/apache/syncope/common/lib/policy/RuleConf.java
----------------------------------------------------------------------
diff --cc common/lib/src/main/java/org/apache/syncope/common/lib/policy/RuleConf.java
index 0000000,0000000..5a99cd1
new file mode 100644
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/RuleConf.java
@@@ -1,0 -1,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.common.lib.policy;
++
++import com.fasterxml.jackson.annotation.JsonTypeInfo;
++
++@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
++public interface RuleConf {
++
++    /**
++     * Give name of related rule instance.
++     *
++     * @return name of this rule instance
++     */
++    String getName();
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/common/lib/src/main/java/org/apache/syncope/common/lib/policy/SyncPolicySpec.java
----------------------------------------------------------------------
diff --cc common/lib/src/main/java/org/apache/syncope/common/lib/policy/SyncPolicySpec.java
index 0000000,0000000..1eb578e
new file mode 100644
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/SyncPolicySpec.java
@@@ -1,0 -1,0 +1,63 @@@
++/*
++ * 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.common.lib.policy;
++
++import com.fasterxml.jackson.annotation.JsonIgnore;
++import com.fasterxml.jackson.annotation.JsonProperty;
++import java.util.HashMap;
++import java.util.Map;
++import javax.xml.bind.annotation.XmlType;
++import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
++import org.apache.syncope.common.lib.jaxb.XmlGenericMapAdapter;
++import org.apache.syncope.common.lib.types.ConflictResolutionAction;
++
++@XmlType
++public class SyncPolicySpec {
++
++    private static final long serialVersionUID = -3144027171719498127L;
++
++    private ConflictResolutionAction conflictResolutionAction;
++
++    /**
++     * Associates anyTypeKey to either:
++     * <ol>
++     * <li>Java class name, implementing {@code SyncCorrelationRule}</li>
++     * <li>JSON array containing plain schema names - this will be used to feed
++     * {@code PlainAttrsSyncCorrelationRule}</li>
++     * </ol>
++     */
++    @XmlJavaTypeAdapter(XmlGenericMapAdapter.class)
++    @JsonIgnore
++    private final Map<String, String> correlationRules = new HashMap<>();
++
++    public ConflictResolutionAction getConflictResolutionAction() {
++        return conflictResolutionAction == null
++                ? ConflictResolutionAction.IGNORE
++                : conflictResolutionAction;
++    }
++
++    public void setConflictResolutionAction(final ConflictResolutionAction conflictResolutionAction) {
++        this.conflictResolutionAction = conflictResolutionAction;
++    }
++
++    @JsonProperty
++    public Map<String, String> getCorrelationRules() {
++        return correlationRules;
++    }
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/common/lib/src/main/java/org/apache/syncope/common/lib/policy/SyncPolicyTO.java
----------------------------------------------------------------------
diff --cc common/lib/src/main/java/org/apache/syncope/common/lib/policy/SyncPolicyTO.java
index 0000000,0000000..911c445
new file mode 100644
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/SyncPolicyTO.java
@@@ -1,0 -1,0 +1,45 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.common.lib.policy;
++
++import javax.xml.bind.annotation.XmlRootElement;
++import javax.xml.bind.annotation.XmlType;
++import org.apache.syncope.common.lib.types.PolicyType;
++
++@XmlRootElement(name = "syncPolicy")
++@XmlType
++public class SyncPolicyTO extends AbstractPolicyTO {
++
++    private static final long serialVersionUID = 993024634238024242L;
++
++    private SyncPolicySpec specification;
++
++    public SyncPolicyTO() {
++        super(PolicyType.SYNC);
++    }
++
++    public SyncPolicySpec getSpecification() {
++        return specification;
++    }
++
++    public void setSpecification(final SyncPolicySpec specification) {
++        this.specification = specification;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/common/lib/src/main/java/org/apache/syncope/common/lib/wrap/ReportletConfClass.java
----------------------------------------------------------------------
diff --cc common/lib/src/main/java/org/apache/syncope/common/lib/wrap/ReportletConfClass.java
index 24ba188,24ba188..0000000
deleted file mode 100644,100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/wrap/ReportletConfClass.java
+++ /dev/null
@@@ -1,30 -1,30 +1,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.common.lib.wrap;
--
--import javax.xml.bind.annotation.XmlRootElement;
--import javax.xml.bind.annotation.XmlType;
--
--@XmlRootElement(name = "reportletConfClass")
--@XmlType
--public class ReportletConfClass extends AbstractWrappable<String> {
--
--    private static final long serialVersionUID = 1343357929074360450L;
--
--}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/misc/src/main/java/org/apache/syncope/core/misc/policy/InvalidPasswordRuleConf.java
----------------------------------------------------------------------
diff --cc core/misc/src/main/java/org/apache/syncope/core/misc/policy/InvalidPasswordRuleConf.java
index 0000000,0000000..4b13807
new file mode 100644
--- /dev/null
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/policy/InvalidPasswordRuleConf.java
@@@ -1,0 -1,0 +1,37 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.core.misc.policy;
++
++/**
++ * Raise when the merge of two or more PasswordRuleconf instances led to an inconsistent condition.
++ *
++ * @see org.apache.syncope.common.lib.policy.PasswordRuleConf
++ */
++public class InvalidPasswordRuleConf extends Exception {
++
++    private static final long serialVersionUID = 4810651743226663580L;
++
++    public InvalidPasswordRuleConf(final String msg) {
++        super(msg);
++    }
++
++    public InvalidPasswordRuleConf(final String msg, final Exception e) {
++        super(msg, e);
++    }
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/misc/src/main/java/org/apache/syncope/core/misc/security/DefaultPasswordGenerator.java
----------------------------------------------------------------------
diff --cc core/misc/src/main/java/org/apache/syncope/core/misc/security/DefaultPasswordGenerator.java
index 0000000,0000000..a1a1822
new file mode 100644
--- /dev/null
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/security/DefaultPasswordGenerator.java
@@@ -1,0 -1,0 +1,330 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.core.misc.security;
++
++import java.util.ArrayList;
++import java.util.List;
++import org.apache.commons.lang3.StringUtils;
++import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf;
++import org.apache.syncope.common.lib.policy.PasswordRuleConf;
++import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
++import org.apache.syncope.core.persistence.api.entity.user.User;
++import org.apache.syncope.core.misc.policy.InvalidPasswordRuleConf;
++import org.apache.syncope.core.misc.policy.PolicyPattern;
++import org.apache.syncope.core.persistence.api.dao.RealmDAO;
++import org.apache.syncope.core.persistence.api.dao.UserDAO;
++import org.apache.syncope.core.persistence.api.entity.Realm;
++import org.springframework.beans.factory.annotation.Autowired;
++
++/**
++ * Generate random passwords according to given policies.
++ * When no minimum and / or maximum length are specified, default values are set.
++ *
++ * <strong>WARNING</strong>: This class only takes {@link DefaultPasswordRuleConf} into account.
++ */
++public class DefaultPasswordGenerator implements PasswordGenerator {
++
++    private static final char[] SPECIAL_CHARS = { '!', '£', '%', '&', '(', ')', '?', '#', '$' };
++
++    private static final int VERY_MIN_LENGTH = 0;
++
++    private static final int VERY_MAX_LENGTH = 64;
++
++    private static final int MIN_LENGTH_IF_ZERO = 6;
++
++    @Autowired
++    private UserDAO userDAO;
++
++    @Autowired
++    private RealmDAO realmDAO;
++
++    @Override
++    public String generate(final User user) throws InvalidPasswordRuleConf {
++        List<PasswordRuleConf> ruleConfs = new ArrayList<>();
++
++        for (Realm ancestor : realmDAO.findAncestors(user.getRealm())) {
++            if (ancestor.getPasswordPolicy() != null) {
++                ruleConfs.addAll(ancestor.getPasswordPolicy().getRuleConfs());
++            }
++        }
++
++        for (ExternalResource resource : userDAO.findAllResources(user)) {
++            if (resource.getPasswordPolicy() != null) {
++                ruleConfs.addAll(resource.getPasswordPolicy().getRuleConfs());
++            }
++        }
++
++        return generate(ruleConfs);
++    }
++
++    @Override
++    public String generate(final List<PasswordRuleConf> ruleConfs) throws InvalidPasswordRuleConf {
++        List<DefaultPasswordRuleConf> defaultRuleConfs = new ArrayList<>();
++        for (PasswordRuleConf ruleConf : ruleConfs) {
++            if (ruleConf instanceof DefaultPasswordRuleConf) {
++                defaultRuleConfs.add((DefaultPasswordRuleConf) ruleConf);
++            }
++        }
++
++        DefaultPasswordRuleConf ruleConf = merge(defaultRuleConfs);
++        check(ruleConf);
++        return generate(ruleConf);
++    }
++
++    private DefaultPasswordRuleConf merge(final List<DefaultPasswordRuleConf> defaultRuleConfs) {
++        DefaultPasswordRuleConf fpps = new DefaultPasswordRuleConf();
++        fpps.setMinLength(VERY_MIN_LENGTH);
++        fpps.setMaxLength(VERY_MAX_LENGTH);
++
++        for (DefaultPasswordRuleConf ruleConf : defaultRuleConfs) {
++            if (ruleConf.getMinLength() > fpps.getMinLength()) {
++                fpps.setMinLength(ruleConf.getMinLength());
++            }
++
++            if ((ruleConf.getMaxLength() != 0) && ((ruleConf.getMaxLength() < fpps.getMaxLength()))) {
++                fpps.setMaxLength(ruleConf.getMaxLength());
++            }
++            fpps.getPrefixesNotPermitted().addAll(ruleConf.getPrefixesNotPermitted());
++            fpps.getSuffixesNotPermitted().addAll(ruleConf.getSuffixesNotPermitted());
++
++            if (!fpps.isNonAlphanumericRequired()) {
++                fpps.setNonAlphanumericRequired(ruleConf.isNonAlphanumericRequired());
++            }
++
++            if (!fpps.isAlphanumericRequired()) {
++                fpps.setAlphanumericRequired(ruleConf.isAlphanumericRequired());
++            }
++            if (!fpps.isDigitRequired()) {
++                fpps.setDigitRequired(ruleConf.isDigitRequired());
++            }
++
++            if (!fpps.isLowercaseRequired()) {
++                fpps.setLowercaseRequired(ruleConf.isLowercaseRequired());
++            }
++            if (!fpps.isUppercaseRequired()) {
++                fpps.setUppercaseRequired(ruleConf.isUppercaseRequired());
++            }
++            if (!fpps.isMustStartWithDigit()) {
++                fpps.setMustStartWithDigit(ruleConf.isMustStartWithDigit());
++            }
++            if (!fpps.isMustntStartWithDigit()) {
++                fpps.setMustntStartWithDigit(ruleConf.isMustntStartWithDigit());
++            }
++            if (!fpps.isMustEndWithDigit()) {
++                fpps.setMustEndWithDigit(ruleConf.isMustEndWithDigit());
++            }
++            if (fpps.isMustntEndWithDigit()) {
++                fpps.setMustntEndWithDigit(ruleConf.isMustntEndWithDigit());
++            }
++            if (!fpps.isMustStartWithAlpha()) {
++                fpps.setMustStartWithAlpha(ruleConf.isMustStartWithAlpha());
++            }
++            if (!fpps.isMustntStartWithAlpha()) {
++                fpps.setMustntStartWithAlpha(ruleConf.isMustntStartWithAlpha());
++            }
++            if (!fpps.isMustStartWithNonAlpha()) {
++                fpps.setMustStartWithNonAlpha(ruleConf.isMustStartWithNonAlpha());
++            }
++            if (!fpps.isMustntStartWithNonAlpha()) {
++                fpps.setMustntStartWithNonAlpha(ruleConf.isMustntStartWithNonAlpha());
++            }
++            if (!fpps.isMustEndWithNonAlpha()) {
++                fpps.setMustEndWithNonAlpha(ruleConf.isMustEndWithNonAlpha());
++            }
++            if (!fpps.isMustntEndWithNonAlpha()) {
++                fpps.setMustntEndWithNonAlpha(ruleConf.isMustntEndWithNonAlpha());
++            }
++            if (!fpps.isMustEndWithAlpha()) {
++                fpps.setMustEndWithAlpha(ruleConf.isMustEndWithAlpha());
++            }
++            if (!fpps.isMustntEndWithAlpha()) {
++                fpps.setMustntEndWithAlpha(ruleConf.isMustntEndWithAlpha());
++            }
++        }
++
++        if (fpps.getMinLength() == 0) {
++            fpps.setMinLength(fpps.getMaxLength() < MIN_LENGTH_IF_ZERO ? fpps.getMaxLength() : MIN_LENGTH_IF_ZERO);
++        }
++
++        return fpps;
++    }
++
++    private void check(final DefaultPasswordRuleConf defaultPasswordRuleConf)
++            throws InvalidPasswordRuleConf {
++
++        if (defaultPasswordRuleConf.isMustEndWithAlpha() && defaultPasswordRuleConf.isMustntEndWithAlpha()) {
++            throw new InvalidPasswordRuleConf(
++                    "mustEndWithAlpha and mustntEndWithAlpha are both true");
++        }
++        if (defaultPasswordRuleConf.isMustEndWithAlpha() && defaultPasswordRuleConf.isMustEndWithDigit()) {
++            throw new InvalidPasswordRuleConf(
++                    "mustEndWithAlpha and mustEndWithDigit are both true");
++        }
++        if (defaultPasswordRuleConf.isMustEndWithDigit() && defaultPasswordRuleConf.isMustntEndWithDigit()) {
++            throw new InvalidPasswordRuleConf(
++                    "mustEndWithDigit and mustntEndWithDigit are both true");
++        }
++        if (defaultPasswordRuleConf.isMustEndWithNonAlpha() && defaultPasswordRuleConf.isMustntEndWithNonAlpha()) {
++            throw new InvalidPasswordRuleConf(
++                    "mustEndWithNonAlpha and mustntEndWithNonAlpha are both true");
++        }
++        if (defaultPasswordRuleConf.isMustStartWithAlpha() && defaultPasswordRuleConf.isMustntStartWithAlpha()) {
++            throw new InvalidPasswordRuleConf(
++                    "mustStartWithAlpha and mustntStartWithAlpha are both true");
++        }
++        if (defaultPasswordRuleConf.isMustStartWithAlpha() && defaultPasswordRuleConf.isMustStartWithDigit()) {
++            throw new InvalidPasswordRuleConf(
++                    "mustStartWithAlpha and mustStartWithDigit are both true");
++        }
++        if (defaultPasswordRuleConf.isMustStartWithDigit() && defaultPasswordRuleConf.isMustntStartWithDigit()) {
++            throw new InvalidPasswordRuleConf(
++                    "mustStartWithDigit and mustntStartWithDigit are both true");
++        }
++        if (defaultPasswordRuleConf.isMustStartWithNonAlpha() && defaultPasswordRuleConf.isMustntStartWithNonAlpha()) {
++            throw new InvalidPasswordRuleConf(
++                    "mustStartWithNonAlpha and mustntStartWithNonAlpha are both true");
++        }
++        if (defaultPasswordRuleConf.getMinLength() > defaultPasswordRuleConf.getMaxLength()) {
++            throw new InvalidPasswordRuleConf(
++                    "Minimun length (" + defaultPasswordRuleConf.getMinLength() + ")"
++                    + "is greater than maximum length (" + defaultPasswordRuleConf.getMaxLength() + ")");
++        }
++    }
++
++    private String generate(final DefaultPasswordRuleConf ruleConf) {
++        String[] generatedPassword = new String[ruleConf.getMinLength()];
++
++        for (int i = 0; i < generatedPassword.length; i++) {
++            generatedPassword[i] = StringUtils.EMPTY;
++        }
++
++        checkStartChar(generatedPassword, ruleConf);
++
++        checkEndChar(generatedPassword, ruleConf);
++
++        checkRequired(generatedPassword, ruleConf);
++
++        for (int firstEmptyChar = firstEmptyChar(generatedPassword);
++                firstEmptyChar < generatedPassword.length - 1; firstEmptyChar++) {
++
++            generatedPassword[firstEmptyChar] = SecureRandomUtils.generateRandomLetter();
++        }
++
++        checkPrefixAndSuffix(generatedPassword, ruleConf);
++
++        return StringUtils.join(generatedPassword);
++    }
++
++    private void checkStartChar(final String[] generatedPassword, final DefaultPasswordRuleConf ruleConf) {
++        if (ruleConf.isMustStartWithAlpha()) {
++            generatedPassword[0] = SecureRandomUtils.generateRandomLetter();
++        }
++        if (ruleConf.isMustStartWithNonAlpha() || ruleConf.isMustStartWithDigit()) {
++            generatedPassword[0] = SecureRandomUtils.generateRandomNumber();
++        }
++        if (ruleConf.isMustntStartWithAlpha()) {
++            generatedPassword[0] = SecureRandomUtils.generateRandomNumber();
++        }
++        if (ruleConf.isMustntStartWithDigit()) {
++            generatedPassword[0] = SecureRandomUtils.generateRandomLetter();
++        }
++        if (ruleConf.isMustntStartWithNonAlpha()) {
++            generatedPassword[0] = SecureRandomUtils.generateRandomLetter();
++        }
++
++        if (StringUtils.EMPTY.equals(generatedPassword[0])) {
++            generatedPassword[0] = SecureRandomUtils.generateRandomLetter();
++        }
++    }
++
++    private void checkEndChar(final String[] generatedPassword, final DefaultPasswordRuleConf ruleConf) {
++        if (ruleConf.isMustEndWithAlpha()) {
++            generatedPassword[ruleConf.getMinLength() - 1] = SecureRandomUtils.generateRandomLetter();
++        }
++        if (ruleConf.isMustEndWithNonAlpha() || ruleConf.isMustEndWithDigit()) {
++            generatedPassword[ruleConf.getMinLength() - 1] = SecureRandomUtils.generateRandomNumber();
++        }
++
++        if (ruleConf.isMustntEndWithAlpha()) {
++            generatedPassword[ruleConf.getMinLength() - 1] = SecureRandomUtils.generateRandomNumber();
++        }
++        if (ruleConf.isMustntEndWithDigit()) {
++            generatedPassword[ruleConf.getMinLength() - 1] = SecureRandomUtils.generateRandomLetter();
++        }
++        if (ruleConf.isMustntEndWithNonAlpha()) {
++            generatedPassword[ruleConf.getMinLength() - 1] = SecureRandomUtils.generateRandomLetter();
++        }
++
++        if (StringUtils.EMPTY.equals(generatedPassword[ruleConf.getMinLength() - 1])) {
++            generatedPassword[ruleConf.getMinLength() - 1] = SecureRandomUtils.generateRandomLetter();
++        }
++    }
++
++    private int firstEmptyChar(final String[] generatedPStrings) {
++        int index = 0;
++        while (!generatedPStrings[index].isEmpty()) {
++            index++;
++        }
++        return index;
++    }
++
++    private void checkRequired(final String[] generatedPassword, final DefaultPasswordRuleConf ruleConf) {
++        if (ruleConf.isDigitRequired()
++                && !PolicyPattern.DIGIT.matcher(StringUtils.join(generatedPassword)).matches()) {
++
++            generatedPassword[firstEmptyChar(generatedPassword)] = SecureRandomUtils.generateRandomNumber();
++        }
++
++        if (ruleConf.isUppercaseRequired()
++                && !PolicyPattern.ALPHA_UPPERCASE.matcher(StringUtils.join(generatedPassword)).matches()) {
++
++            generatedPassword[firstEmptyChar(generatedPassword)] =
++                    SecureRandomUtils.generateRandomLetter().toUpperCase();
++        }
++
++        if (ruleConf.isLowercaseRequired()
++                && !PolicyPattern.ALPHA_LOWERCASE.matcher(StringUtils.join(generatedPassword)).matches()) {
++
++            generatedPassword[firstEmptyChar(generatedPassword)] =
++                    SecureRandomUtils.generateRandomLetter().toLowerCase();
++        }
++
++        if (ruleConf.isNonAlphanumericRequired()
++                && !PolicyPattern.NON_ALPHANUMERIC.matcher(StringUtils.join(generatedPassword)).matches()) {
++
++            generatedPassword[firstEmptyChar(generatedPassword)] =
++                    SecureRandomUtils.generateRandomSpecialCharacter(SPECIAL_CHARS);
++        }
++    }
++
++    private void checkPrefixAndSuffix(final String[] generatedPassword, final DefaultPasswordRuleConf ruleConf) {
++        for (String prefix : ruleConf.getPrefixesNotPermitted()) {
++            if (StringUtils.join(generatedPassword).startsWith(prefix)) {
++                checkStartChar(generatedPassword, ruleConf);
++            }
++        }
++
++        for (String suffix : ruleConf.getSuffixesNotPermitted()) {
++            if (StringUtils.join(generatedPassword).endsWith(suffix)) {
++                checkEndChar(generatedPassword, ruleConf);
++            }
++        }
++    }
++
++}

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

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AccountRuleConfClass.java
----------------------------------------------------------------------
diff --cc core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AccountRuleConfClass.java
index 0000000,0000000..5b6bace
new file mode 100644
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AccountRuleConfClass.java
@@@ -1,0 -1,0 +1,32 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.core.persistence.api.dao;
++
++import java.lang.annotation.ElementType;
++import java.lang.annotation.Retention;
++import java.lang.annotation.RetentionPolicy;
++import java.lang.annotation.Target;
++import org.apache.syncope.common.lib.policy.AccountRuleConf;
++
++@Target({ ElementType.TYPE })
++@Retention(RetentionPolicy.RUNTIME)
++public @interface AccountRuleConfClass {
++
++    Class<? extends AccountRuleConf> value();
++}

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

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PasswordRuleConfClass.java
----------------------------------------------------------------------
diff --cc core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PasswordRuleConfClass.java
index 0000000,0000000..745e336
new file mode 100644
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PasswordRuleConfClass.java
@@@ -1,0 -1,0 +1,32 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.core.persistence.api.dao;
++
++import java.lang.annotation.ElementType;
++import java.lang.annotation.Retention;
++import java.lang.annotation.RetentionPolicy;
++import java.lang.annotation.Target;
++import org.apache.syncope.common.lib.policy.PasswordRuleConf;
++
++@Target({ ElementType.TYPE })
++@Retention(RetentionPolicy.RUNTIME)
++public @interface PasswordRuleConfClass {
++
++    Class<? extends PasswordRuleConf> value();
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/AccountPolicy.java
----------------------------------------------------------------------
diff --cc core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/AccountPolicy.java
index 0000000,0000000..f9a8277
new file mode 100644
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/AccountPolicy.java
@@@ -1,0 -1,0 +1,50 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.core.persistence.api.entity.policy;
++
++import java.util.List;
++import java.util.Set;
++import org.apache.syncope.common.lib.policy.AccountRuleConf;
++import org.apache.syncope.core.persistence.api.entity.Policy;
++import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
++
++public interface AccountPolicy extends Policy {
++
++    boolean isPropagateSuspension();
++
++    void setPropagateSuspension(boolean propagateSuspension);
++
++    int getMaxAuthenticationAttempts();
++
++    void setMaxAuthenticationAttempts(int maxAuthenticationAttempts);
++
++    boolean add(AccountRuleConf accountRuleConf);
++
++    boolean remove(AccountRuleConf accountRuleConf);
++
++    List<AccountRuleConf> getRuleConfs();
++
++    boolean add(ExternalResource resource);
++
++    boolean remove(ExternalResource resource);
++
++    Set<String> getResourceNames();
++
++    Set<? extends ExternalResource> getResources();
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PasswordPolicy.java
----------------------------------------------------------------------
diff --cc core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PasswordPolicy.java
index 0000000,0000000..173608e
new file mode 100644
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PasswordPolicy.java
@@@ -1,0 -1,0 +1,40 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.core.persistence.api.entity.policy;
++
++import java.util.List;
++import org.apache.syncope.common.lib.policy.PasswordRuleConf;
++import org.apache.syncope.core.persistence.api.entity.Policy;
++
++public interface PasswordPolicy extends Policy {
++
++    boolean isAllowNullPassword();
++
++    void setAllowNullPassword(final boolean allowNullPassword);
++
++    int getHistoryLength();
++
++    void setHistoryLength(int historyLength);
++
++    boolean add(PasswordRuleConf passwordRuleConf);
++
++    boolean remove(PasswordRuleConf passwordRuleConf);
++
++    List<PasswordRuleConf> getRuleConfs();
++}

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

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

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultAccountRule.java
----------------------------------------------------------------------
diff --cc core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultAccountRule.java
index 0000000,0000000..7ef2ebf
new file mode 100644
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultAccountRule.java
@@@ -1,0 -1,0 +1,112 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.core.persistence.jpa.dao;
++
++import java.util.List;
++import java.util.regex.Pattern;
++import org.apache.syncope.common.lib.policy.AccountRuleConf;
++import org.apache.syncope.common.lib.policy.DefaultAccountRuleConf;
++import org.apache.syncope.core.misc.policy.AccountPolicyException;
++import org.apache.syncope.core.persistence.api.dao.AccountRule;
++import org.apache.syncope.core.persistence.api.dao.AccountRuleConfClass;
++import org.apache.syncope.core.persistence.api.entity.PlainAttr;
++import org.apache.syncope.core.persistence.api.entity.user.User;
++import org.springframework.transaction.annotation.Transactional;
++
++@AccountRuleConfClass(DefaultAccountRuleConf.class)
++public class DefaultAccountRule implements AccountRule {
++
++    private static final Pattern DEFAULT_PATTERN = Pattern.compile("[a-zA-Z0-9-_@. ]+");
++
++    private DefaultAccountRuleConf conf;
++
++    @Override
++    public void setConf(final AccountRuleConf conf) {
++        if (conf instanceof DefaultAccountRuleConf) {
++            this.conf = (DefaultAccountRuleConf) conf;
++        } else {
++            throw new IllegalArgumentException(
++                    AccountRuleConf.class.getName() + " expected, got " + conf.getClass().getName());
++        }
++    }
++
++    @Transactional(readOnly = true)
++    @Override
++    public void isCompliant(final User user) {
++        for (String schema : conf.getSchemasNotPermitted()) {
++            PlainAttr<?> attr = user.getPlainAttr(schema);
++            if (attr != null) {
++                List<String> values = attr.getValuesAsStrings();
++                if (values != null && !values.isEmpty()) {
++                    conf.getWordsNotPermitted().add(values.get(0));
++                }
++            }
++        }
++
++        if (user.getUsername() == null) {
++            throw new AccountPolicyException("Invalid account");
++        }
++
++        // check min length
++        if (conf.getMinLength() > 0 && conf.getMinLength() > user.getUsername().length()) {
++            throw new AccountPolicyException("Username too short");
++        }
++
++        // check max length
++        if (conf.getMaxLength() > 0 && conf.getMaxLength() < user.getUsername().length()) {
++            throw new AccountPolicyException("Username too long");
++        }
++
++        // check words not permitted
++        for (String word : conf.getWordsNotPermitted()) {
++            if (user.getUsername().contains(word)) {
++                throw new AccountPolicyException("Used word(s) not permitted");
++            }
++        }
++
++        // check case
++        if (conf.isAllUpperCase() && !user.getUsername().equals(user.getUsername().toUpperCase())) {
++            throw new AccountPolicyException("No lowercase characters permitted");
++        }
++        if (conf.isAllLowerCase() && !user.getUsername().equals(user.getUsername().toLowerCase())) {
++            throw new AccountPolicyException("No uppercase characters permitted");
++        }
++
++        // check pattern
++        Pattern pattern = (conf.getPattern() == null) ? DEFAULT_PATTERN : Pattern.compile(conf.getPattern());
++        if (!pattern.matcher(user.getUsername()).matches()) {
++            throw new AccountPolicyException("Username does not match pattern");
++        }
++
++        // check prefix
++        for (String prefix : conf.getPrefixesNotPermitted()) {
++            if (user.getUsername().startsWith(prefix)) {
++                throw new AccountPolicyException("Prefix not permitted");
++            }
++        }
++
++        // check suffix
++        for (String suffix : conf.getSuffixesNotPermitted()) {
++            if (user.getUsername().endsWith(suffix)) {
++                throw new AccountPolicyException("Suffix not permitted");
++            }
++        }
++    }
++
++}


[05/33] syncope git commit: Some small cleanings

Posted by md...@apache.org.
Some small cleanings


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

Branch: refs/heads/SYNCOPE-156
Commit: 56568a43c8cd56393bd3a1d58d3e307595c80de4
Parents: a4400ff
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Tue Aug 18 16:54:31 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Tue Aug 18 16:54:31 2015 +0200

----------------------------------------------------------------------
 .../common/lib/types/AbstractPolicySpec.java    |  2 +-
 .../common/lib/types/EntityViolationType.java   |  3 ---
 .../syncope/common/lib/types/PolicySpec.java    | 25 --------------------
 .../common/lib/types/PushPolicySpec.java        |  2 +-
 .../common/lib/types/SyncPolicySpec.java        |  2 +-
 .../core/logic/report/ReportJobDelegate.java    |  2 +-
 .../core/misc/policy/PolicyEvaluator.java       |  4 ++--
 .../core/persistence/api/entity/Report.java     | 15 ++++++------
 .../persistence/jpa/dao/JPAReportExecDAO.java   |  2 +-
 .../core/persistence/jpa/entity/JPAReport.java  |  8 +++----
 .../core/persistence/jpa/inner/ReportTest.java  |  4 ++--
 .../core/persistence/jpa/outer/ReportTest.java  |  2 +-
 .../java/data/ReportDataBinderImpl.java         |  4 ++--
 13 files changed, 24 insertions(+), 51 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/56568a43/common/lib/src/main/java/org/apache/syncope/common/lib/types/AbstractPolicySpec.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/AbstractPolicySpec.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/AbstractPolicySpec.java
index 33a0c62..e951419 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/AbstractPolicySpec.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/AbstractPolicySpec.java
@@ -30,7 +30,7 @@ import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
 
 @XmlType
-public abstract class AbstractPolicySpec implements PolicySpec {
+public abstract class AbstractPolicySpec {
 
     private static final long serialVersionUID = -6210646284287392063L;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/56568a43/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java
index 6e05a52..a97f510 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/EntityViolationType.java
@@ -24,7 +24,6 @@ import javax.xml.bind.annotation.XmlEnum;
 public enum EntityViolationType {
 
     Standard(""),
-    InvalidAccountPolicy("org.apache.syncope.core.persistence.validation.accountpolicy"),
     InvalidAnyType("org.apache.syncope.core.persistence.validation.anytype"),
     InvalidConnInstanceLocation("org.apache.syncope.core.persistence.validation.conninstance.location"),
     InvalidConnPoolConf("org.apache.syncope.core.persistence.validation.conninstance.poolConf"),
@@ -32,7 +31,6 @@ public enum EntityViolationType {
     InvalidName("org.apache.syncope.core.persistence.validation.name"),
     InvalidNotification("org.apache.syncope.core.persistence.validation.notification"),
     InvalidPassword("org.apache.syncope.core.persistence.validation.user.password"),
-    InvalidPasswordPolicy("org.apache.syncope.core.persistence.validation.passwordpolicy"),
     InvalidPolicy("org.apache.syncope.core.persistence.validation.policy"),
     InvalidPropagationTask("org.apache.syncope.core.persistence.validation.propagationtask"),
     InvalidRealm("org.apache.syncope.core.persistence.validation.realm"),
@@ -44,7 +42,6 @@ public enum EntityViolationType {
     InvalidSchemaMultivalueUnique("org.apache.syncope.core.persistence.validation.schema.multivalueUnique"),
     InvalidSchedTask("org.apache.syncope.core.persistence.validation.schedtask"),
     InvalidSyncTask("org.apache.syncope.core.persistence.validation.synctask"),
-    InvalidSyncPolicy("org.apache.syncope.core.persistence.validation.syncpolicy"),
     InvalidPlainSchema("org.apache.syncope.core.persistence.validation.attrvalue.plainSchema"),
     InvalidDerSchema("org.apache.syncope.core.persistence.validation.attrvalue.derSchema"),
     InvalidVirSchema("org.apache.syncope.core.persistence.validation.attrvalue.virSchema"),

http://git-wip-us.apache.org/repos/asf/syncope/blob/56568a43/common/lib/src/main/java/org/apache/syncope/common/lib/types/PolicySpec.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/PolicySpec.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/PolicySpec.java
deleted file mode 100644
index 7de0e48..0000000
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/PolicySpec.java
+++ /dev/null
@@ -1,25 +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.common.lib.types;
-
-import java.io.Serializable;
-
-public interface PolicySpec extends Serializable {
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/56568a43/common/lib/src/main/java/org/apache/syncope/common/lib/types/PushPolicySpec.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/PushPolicySpec.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/PushPolicySpec.java
index d7a5640..0bbea85 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/PushPolicySpec.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/PushPolicySpec.java
@@ -21,7 +21,7 @@ package org.apache.syncope.common.lib.types;
 import javax.xml.bind.annotation.XmlType;
 
 @XmlType
-public class PushPolicySpec implements PolicySpec {
+public class PushPolicySpec {
 
     private static final long serialVersionUID = 3641030189482617497L;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/56568a43/common/lib/src/main/java/org/apache/syncope/common/lib/types/SyncPolicySpec.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/SyncPolicySpec.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/SyncPolicySpec.java
index 5a3d309..7bdaea0 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/SyncPolicySpec.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/SyncPolicySpec.java
@@ -27,7 +27,7 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 import org.apache.syncope.common.lib.jaxb.XmlGenericMapAdapter;
 
 @XmlType
-public class SyncPolicySpec implements PolicySpec {
+public class SyncPolicySpec {
 
     private static final long serialVersionUID = -3144027171719498127L;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/56568a43/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java
index 9920c1f..f7a8def 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java
@@ -87,7 +87,7 @@ public class ReportJobDelegate {
         execution.setReport(report);
         execution = reportExecDAO.save(execution);
 
-        report.addExec(execution);
+        report.add(execution);
         report = reportDAO.save(report);
 
         // 2. define a SAX handler for generating result as XML

http://git-wip-us.apache.org/repos/asf/syncope/blob/56568a43/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PolicyEvaluator.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PolicyEvaluator.java b/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PolicyEvaluator.java
index f7d946c..48cc981 100644
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PolicyEvaluator.java
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PolicyEvaluator.java
@@ -19,9 +19,9 @@
 package org.apache.syncope.core.misc.policy;
 
 import java.util.List;
+import org.apache.syncope.common.lib.types.AbstractPolicySpec;
 import org.apache.syncope.common.lib.types.AccountPolicySpec;
 import org.apache.syncope.common.lib.types.PasswordPolicySpec;
-import org.apache.syncope.common.lib.types.PolicySpec;
 import org.apache.syncope.core.persistence.api.entity.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.persistence.api.entity.PasswordPolicy;
@@ -39,7 +39,7 @@ public class PolicyEvaluator {
     private static final Logger LOG = LoggerFactory.getLogger(PolicyEvaluator.class);
 
     @SuppressWarnings("unchecked")
-    public <T extends PolicySpec> T evaluate(final Policy policy, final Any<?, ?, ?> any) {
+    public <T extends AbstractPolicySpec> T evaluate(final Policy policy, final Any<?, ?, ?> any) {
         T result;
         switch (policy.getType()) {
             case PASSWORD:

http://git-wip-us.apache.org/repos/asf/syncope/blob/56568a43/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Report.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Report.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Report.java
index a2605e7..618e612 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Report.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Report.java
@@ -25,21 +25,22 @@ public interface Report extends Entity<Long> {
 
     String getName();
 
-    boolean addExec(ReportExec exec);
+    void setName(String name);
 
-    boolean addReportletConf(ReportletConf reportletConf);
+    boolean add(ReportExec exec);
 
-    String getCronExpression();
+    boolean remove(ReportExec exec);
 
     List<? extends ReportExec> getExecs();
 
-    List<? extends ReportletConf> getReportletConfs();
+    boolean add(ReportletConf reportletConf);
 
-    boolean removeExec(ReportExec exec);
+    boolean remove(ReportletConf reportletConf);
 
-    boolean removeReportletConf(ReportletConf reportletConf);
+    List<? extends ReportletConf> getReportletConfs();
+
+    String getCronExpression();
 
     void setCronExpression(String cronExpression);
 
-    void setName(String name);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/56568a43/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAReportExecDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAReportExecDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAReportExecDAO.java
index cf9e781..35b7556 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAReportExecDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAReportExecDAO.java
@@ -91,7 +91,7 @@ public class JPAReportExecDAO extends AbstractDAO<ReportExec, Long> implements R
     @Override
     public void delete(final ReportExec execution) {
         if (execution.getReport() != null) {
-            execution.getReport().removeExec(execution);
+            execution.getReport().remove(execution);
         }
 
         entityManager().remove(execution);

http://git-wip-us.apache.org/repos/asf/syncope/blob/56568a43/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java
index 544868f..15c33ea 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java
@@ -82,13 +82,13 @@ public class JPAReport extends AbstractEntity<Long> implements Report {
     }
 
     @Override
-    public boolean addExec(final ReportExec exec) {
+    public boolean add(final ReportExec exec) {
         checkType(exec, JPAReportExec.class);
         return exec != null && !executions.contains((JPAReportExec) exec) && executions.add((JPAReportExec) exec);
     }
 
     @Override
-    public boolean removeExec(final ReportExec exec) {
+    public boolean remove(final ReportExec exec) {
         checkType(exec, JPAReportExec.class);
         return exec != null && executions.remove((JPAReportExec) exec);
     }
@@ -99,7 +99,7 @@ public class JPAReport extends AbstractEntity<Long> implements Report {
     }
 
     @Override
-    public boolean addReportletConf(final ReportletConf reportletConf) {
+    public boolean add(final ReportletConf reportletConf) {
         if (reportletConf == null) {
             return false;
         }
@@ -112,7 +112,7 @@ public class JPAReport extends AbstractEntity<Long> implements Report {
     }
 
     @Override
-    public boolean removeReportletConf(final ReportletConf reportletConf) {
+    public boolean remove(final ReportletConf reportletConf) {
         return CollectionUtils.filter(reportletConfs, new Predicate<JPAReportletConfInstance>() {
 
             @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/56568a43/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ReportTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ReportTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ReportTest.java
index 1144981..536d74f 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ReportTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/ReportTest.java
@@ -59,8 +59,8 @@ public class ReportTest extends AbstractTest {
 
         Report report = entityFactory.newEntity(Report.class);
         report.setName("new report");
-        report.addReportletConf(new UserReportletConf("first"));
-        report.addReportletConf(new UserReportletConf("second"));
+        report.add(new UserReportletConf("first"));
+        report.add(new UserReportletConf("second"));
 
         report = reportDAO.save(report);
         assertNotNull(report);

http://git-wip-us.apache.org/repos/asf/syncope/blob/56568a43/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ReportTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ReportTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ReportTest.java
index f143fd8..36f275c 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ReportTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ReportTest.java
@@ -80,7 +80,7 @@ public class ReportTest extends AbstractTest {
         reportExec.setEndDate(new Date());
         reportExec.setStatus(ReportExecStatus.SUCCESS);
 
-        report.addExec(reportExec);
+        report.add(reportExec);
 
         reportExec = reportExecDAO.save(reportExec);
         assertNotNull(reportExec);

http://git-wip-us.apache.org/repos/asf/syncope/blob/56568a43/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ReportDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ReportDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ReportDataBinderImpl.java
index e98bc7d..6a40803 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ReportDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ReportDataBinderImpl.java
@@ -68,12 +68,12 @@ public class ReportDataBinderImpl implements ReportDataBinder {
             toRemove.add(conf);
         }
         for (ReportletConf conf : toRemove) {
-            report.removeReportletConf(conf);
+            report.remove(conf);
         }
 
         // 2. take all reportlet confs from reportTO
         for (ReportletConf conf : reportTO.getReportletConfs()) {
-            report.addReportletConf(conf);
+            report.add(conf);
         }
     }
 


[09/33] syncope git commit: Restoring Travis CI notifications via e-mail

Posted by md...@apache.org.
Restoring Travis CI notifications via e-mail


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

Branch: refs/heads/SYNCOPE-156
Commit: 4ae3e2cb40980702268041d4d9c961a41797a6cd
Parents: dff48fd bc41d9b
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Thu Aug 20 11:39:57 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Thu Aug 20 11:39:57 2015 +0200

----------------------------------------------------------------------
 .travis.yml | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/4ae3e2cb/.travis.yml
----------------------------------------------------------------------
diff --cc .travis.yml
index e9245f1,68bdf96..7e98db5
--- a/.travis.yml
+++ b/.travis.yml
@@@ -17,8 -17,9 +17,9 @@@ language: jav
  jdk:
    - openjdk6
  # default install is mvn install --quiet -DskipTests=true
 -install: mvn --show-version --errors -P skipTests
 +install: mvn --show-version --quiet -P skipTests
  #invoker.streamLogs: we cannot access to log files through Travis web ui, so display everything in the console
 -script: mvn --show-version --errors clean install -Dinvoker.streamLogs=true
 +script: mvn --show-version --quiet clean install -Dinvoker.streamLogs=true
  notifications:
-   webhooks: http://rovere.tirasa.net/cgi-bin/travis.cgi
+   email:
+     - dev@syncope.apache.org


[04/33] syncope git commit: Upgrading highlightjs

Posted by md...@apache.org.
Upgrading highlightjs


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

Branch: refs/heads/SYNCOPE-156
Commit: a4400ffee35418df3d87603c73fda28474f1ad12
Parents: 2cf7619
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Tue Aug 18 14:56:07 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Tue Aug 18 14:56:07 2015 +0200

----------------------------------------------------------------------
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/a4400ffe/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 6330b9e..d50da6b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -378,7 +378,7 @@ under the License.
     <bootstrap-select.version>1.6.3</bootstrap-select.version>
     <font-awesome.version>4.4.0</font-awesome.version>
     <ionicons.version>2.0.1</ionicons.version>
-    <highlightjs.version>8.4-4</highlightjs.version>
+    <highlightjs.version>8.7</highlightjs.version>
     <codemirror.version>5.5</codemirror.version>
     <jsplumb.version>1.7.5</jsplumb.version>
     


[11/33] syncope git commit: Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/syncope

Posted by md...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPasswordRule.java
----------------------------------------------------------------------
diff --cc core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPasswordRule.java
index 0000000,0000000..8eaad0e
new file mode 100644
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPasswordRule.java
@@@ -1,0 -1,0 +1,218 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.core.persistence.jpa.dao;
++
++import java.util.List;
++import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf;
++import org.apache.syncope.common.lib.policy.PasswordRuleConf;
++import org.apache.syncope.core.misc.policy.PasswordPolicyException;
++import org.apache.syncope.core.misc.policy.PolicyPattern;
++import org.apache.syncope.core.persistence.api.dao.PasswordRule;
++import org.apache.syncope.core.persistence.api.dao.PasswordRuleConfClass;
++import org.apache.syncope.core.persistence.api.entity.PlainAttr;
++import org.apache.syncope.core.persistence.api.entity.user.User;
++import org.springframework.transaction.annotation.Transactional;
++
++@PasswordRuleConfClass(DefaultPasswordRuleConf.class)
++public class DefaultPasswordRule implements PasswordRule {
++
++    private DefaultPasswordRuleConf conf;
++
++    @Override
++    public void setConf(final PasswordRuleConf conf) {
++        if (conf instanceof DefaultPasswordRuleConf) {
++            this.conf = (DefaultPasswordRuleConf) conf;
++        } else {
++            throw new IllegalArgumentException(
++                    PasswordRuleConf.class.getName() + " expected, got " + conf.getClass().getName());
++        }
++    }
++
++    @Transactional(readOnly = true)
++    @Override
++    public void isCompliant(final User user) {
++        for (String schema : conf.getSchemasNotPermitted()) {
++            PlainAttr<?> attr = user.getPlainAttr(schema);
++            if (attr != null) {
++                List<String> values = attr.getValuesAsStrings();
++                if (values != null && !values.isEmpty()) {
++                    conf.getWordsNotPermitted().add(values.get(0));
++                }
++            }
++        }
++
++        String clearPassword = user.getClearPassword();
++        String password = user.getPassword();
++
++        if (password != null && clearPassword != null) {
++            // check length
++            if (conf.getMinLength() > 0 && conf.getMinLength() > clearPassword.length()) {
++                throw new PasswordPolicyException("Password too short");
++            }
++
++            if (conf.getMaxLength() > 0 && conf.getMaxLength() < clearPassword.length()) {
++                throw new PasswordPolicyException("Password too long");
++            }
++
++            // check words not permitted
++            for (String word : conf.getWordsNotPermitted()) {
++                if (clearPassword.contains(word)) {
++                    throw new PasswordPolicyException("Used word(s) not permitted");
++                }
++            }
++
++            // check digits occurrence
++            if (conf.isDigitRequired() && !checkDigit(clearPassword)) {
++                throw new PasswordPolicyException("Password must contain digit(s)");
++            }
++
++            // check lowercase alphabetic characters occurrence
++            if (conf.isLowercaseRequired() && !checkLowercase(clearPassword)) {
++                throw new PasswordPolicyException("Password must contain lowercase alphabetic character(s)");
++            }
++
++            // check uppercase alphabetic characters occurrence
++            if (conf.isUppercaseRequired() && !checkUppercase(clearPassword)) {
++                throw new PasswordPolicyException("Password must contain uppercase alphabetic character(s)");
++            }
++
++            // check prefix
++            for (String prefix : conf.getPrefixesNotPermitted()) {
++                if (clearPassword.startsWith(prefix)) {
++                    throw new PasswordPolicyException("Prefix not permitted");
++                }
++            }
++
++            // check suffix
++            for (String suffix : conf.getSuffixesNotPermitted()) {
++                if (clearPassword.endsWith(suffix)) {
++                    throw new PasswordPolicyException("Suffix not permitted");
++                }
++            }
++
++            // check digit first occurrence
++            if (conf.isMustStartWithDigit() && !checkFirstDigit(clearPassword)) {
++                throw new PasswordPolicyException("Password must start with a digit");
++            }
++
++            if (conf.isMustntStartWithDigit() && checkFirstDigit(clearPassword)) {
++                throw new PasswordPolicyException("Password mustn't start with a digit");
++            }
++
++            // check digit last occurrence
++            if (conf.isMustEndWithDigit() && !checkLastDigit(clearPassword)) {
++                throw new PasswordPolicyException("Password must end with a digit");
++            }
++
++            if (conf.isMustntEndWithDigit() && checkLastDigit(clearPassword)) {
++                throw new PasswordPolicyException("Password mustn't end with a digit");
++            }
++
++            // check alphanumeric characters occurence
++            if (conf.isAlphanumericRequired() && !checkAlphanumeric(clearPassword)) {
++                throw new PasswordPolicyException("Password must contain alphanumeric character(s)");
++            }
++
++            // check non alphanumeric characters occurence
++            if (conf.isNonAlphanumericRequired() && !checkNonAlphanumeric(clearPassword)) {
++                throw new PasswordPolicyException("Password must contain non-alphanumeric character(s)");
++            }
++
++            // check alphanumeric character first occurrence
++            if (conf.isMustStartWithAlpha() && !checkFirstAlphanumeric(clearPassword)) {
++                throw new PasswordPolicyException("Password must start with an alphanumeric character");
++            }
++
++            if (conf.isMustntStartWithAlpha() && checkFirstAlphanumeric(clearPassword)) {
++                throw new PasswordPolicyException("Password mustn't start with an alphanumeric character");
++            }
++
++            // check alphanumeric character last occurrence
++            if (conf.isMustEndWithAlpha() && !checkLastAlphanumeric(clearPassword)) {
++                throw new PasswordPolicyException("Password must end with an alphanumeric character");
++            }
++
++            if (conf.isMustntEndWithAlpha() && checkLastAlphanumeric(clearPassword)) {
++                throw new PasswordPolicyException("Password mustn't end with an alphanumeric character");
++            }
++
++            // check non alphanumeric character first occurrence
++            if (conf.isMustStartWithNonAlpha() && !checkFirstNonAlphanumeric(clearPassword)) {
++                throw new PasswordPolicyException("Password must start with a non-alphanumeric character");
++            }
++
++            if (conf.isMustntStartWithNonAlpha() && checkFirstNonAlphanumeric(clearPassword)) {
++                throw new PasswordPolicyException("Password mustn't start with a non-alphanumeric character");
++            }
++
++            // check non alphanumeric character last occurrence
++            if (conf.isMustEndWithNonAlpha() && !checkLastNonAlphanumeric(clearPassword)) {
++                throw new PasswordPolicyException("Password must end with a non-alphanumeric character");
++            }
++
++            if (conf.isMustntEndWithNonAlpha() && checkLastNonAlphanumeric(clearPassword)) {
++                throw new PasswordPolicyException("Password mustn't end with a non-alphanumeric character");
++            }
++        }
++    }
++
++    private boolean checkDigit(final String str) {
++        return PolicyPattern.DIGIT.matcher(str).matches();
++    }
++
++    private boolean checkLowercase(final String str) {
++        return PolicyPattern.ALPHA_LOWERCASE.matcher(str).matches();
++    }
++
++    private boolean checkUppercase(final String str) {
++        return PolicyPattern.ALPHA_UPPERCASE.matcher(str).matches();
++    }
++
++    private boolean checkFirstDigit(final String str) {
++        return PolicyPattern.FIRST_DIGIT.matcher(str).matches();
++    }
++
++    private boolean checkLastDigit(final String str) {
++        return PolicyPattern.LAST_DIGIT.matcher(str).matches();
++    }
++
++    private boolean checkAlphanumeric(final String str) {
++        return PolicyPattern.ALPHANUMERIC.matcher(str).matches();
++    }
++
++    private boolean checkFirstAlphanumeric(final String str) {
++        return PolicyPattern.FIRST_ALPHANUMERIC.matcher(str).matches();
++    }
++
++    private boolean checkLastAlphanumeric(final String str) {
++        return PolicyPattern.LAST_ALPHANUMERIC.matcher(str).matches();
++    }
++
++    private boolean checkNonAlphanumeric(final String str) {
++        return PolicyPattern.NON_ALPHANUMERIC.matcher(str).matches();
++    }
++
++    private boolean checkFirstNonAlphanumeric(final String str) {
++        return PolicyPattern.FIRST_NON_ALPHANUMERIC.matcher(str).matches();
++    }
++
++    private boolean checkLastNonAlphanumeric(final String str) {
++        return PolicyPattern.LAST_NON_ALPHANUMERIC.matcher(str).matches();
++    }
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/AbstractPolicy.java
----------------------------------------------------------------------
diff --cc core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/AbstractPolicy.java
index 0000000,0000000..843bbcd
new file mode 100644
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/AbstractPolicy.java
@@@ -1,0 -1,0 +1,67 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.core.persistence.jpa.entity.policy;
++
++import javax.persistence.Entity;
++import javax.persistence.EnumType;
++import javax.persistence.Enumerated;
++import javax.persistence.Id;
++import javax.persistence.Inheritance;
++import javax.persistence.InheritanceType;
++import javax.validation.constraints.NotNull;
++import org.apache.syncope.common.lib.types.PolicyType;
++import org.apache.syncope.core.persistence.api.entity.Policy;
++import org.apache.syncope.core.persistence.jpa.entity.AbstractEntity;
++
++@Entity
++@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
++public abstract class AbstractPolicy extends AbstractEntity<Long> implements Policy {
++
++    private static final long serialVersionUID = -5844833125843247458L;
++
++    @Id
++    private Long id;
++
++    @NotNull
++    private String description;
++
++    @NotNull
++    @Enumerated(EnumType.STRING)
++    protected PolicyType type;
++
++    @Override
++    public Long getKey() {
++        return id;
++    }
++
++    @Override
++    public String getDescription() {
++        return description;
++    }
++
++    @Override
++    public void setDescription(final String description) {
++        this.description = description;
++    }
++
++    @Override
++    public PolicyType getType() {
++        return type;
++    }
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java
----------------------------------------------------------------------
diff --cc core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java
index 0000000,0000000..87aa04f
new file mode 100644
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java
@@@ -1,0 -1,0 +1,160 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.core.persistence.jpa.entity.policy;
++
++import java.util.ArrayList;
++import java.util.HashSet;
++import java.util.List;
++import java.util.Set;
++import javax.persistence.Basic;
++import javax.persistence.CascadeType;
++import javax.persistence.Entity;
++import javax.persistence.FetchType;
++import javax.persistence.JoinColumn;
++import javax.persistence.JoinTable;
++import javax.persistence.ManyToMany;
++import javax.persistence.OneToMany;
++import javax.persistence.Table;
++import javax.validation.constraints.Max;
++import javax.validation.constraints.Min;
++import org.apache.commons.collections4.CollectionUtils;
++import org.apache.commons.collections4.Predicate;
++import org.apache.commons.collections4.Transformer;
++import org.apache.syncope.common.lib.policy.AccountRuleConf;
++import org.apache.syncope.common.lib.types.PolicyType;
++import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
++import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
++import org.apache.syncope.core.persistence.jpa.entity.resource.JPAExternalResource;
++
++@Entity
++@Table(name = JPAAccountPolicy.TABLE)
++public class JPAAccountPolicy extends AbstractPolicy implements AccountPolicy {
++
++    private static final long serialVersionUID = -2767606675667839060L;
++
++    public static final String TABLE = "AccountPolicy";
++
++    @Basic
++    @Min(0)
++    @Max(1)
++    private Integer propagateSuspension;
++
++    private int maxAuthenticationAttempts;
++
++    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "accountPolicy")
++    private List<JPAAccountRuleConfInstance> ruleConfs;
++
++    /**
++     * Resources for alternative user authentication: if empty, only internal storage will be used.
++     */
++    @ManyToMany(fetch = FetchType.EAGER)
++    @JoinTable(joinColumns =
++            @JoinColumn(name = "accountPolicy_id"),
++            inverseJoinColumns =
++            @JoinColumn(name = "resource_name"))
++    private Set<JPAExternalResource> resources = new HashSet<>();
++
++    public JPAAccountPolicy() {
++        super();
++        this.type = PolicyType.ACCOUNT;
++    }
++
++    @Override
++    public boolean isPropagateSuspension() {
++        return isBooleanAsInteger(propagateSuspension);
++    }
++
++    @Override
++    public void setPropagateSuspension(final boolean propagateSuspension) {
++        this.propagateSuspension = getBooleanAsInteger(propagateSuspension);
++    }
++
++    @Override
++    public int getMaxAuthenticationAttempts() {
++        return maxAuthenticationAttempts;
++    }
++
++    @Override
++    public void setMaxAuthenticationAttempts(final int maxAuthenticationAttempts) {
++        this.maxAuthenticationAttempts = maxAuthenticationAttempts;
++    }
++
++    @Override
++    public boolean add(final AccountRuleConf accountRuleConf) {
++        if (accountRuleConf == null) {
++            return false;
++        }
++
++        JPAAccountRuleConfInstance instance = new JPAAccountRuleConfInstance();
++        instance.setAccountPolicy(this);
++        instance.setInstance(accountRuleConf);
++
++        return ruleConfs.add(instance);
++    }
++
++    @Override
++    public boolean remove(final AccountRuleConf accountRuleConf) {
++        return CollectionUtils.filter(ruleConfs, new Predicate<JPAAccountRuleConfInstance>() {
++
++            @Override
++            public boolean evaluate(final JPAAccountRuleConfInstance object) {
++                return accountRuleConf.equals(object.getInstance());
++            }
++        });
++    }
++
++    @Override
++    public List<AccountRuleConf> getRuleConfs() {
++        return CollectionUtils.collect(ruleConfs, new Transformer<JPAAccountRuleConfInstance, AccountRuleConf>() {
++
++            @Override
++            public AccountRuleConf transform(final JPAAccountRuleConfInstance input) {
++                return input.getInstance();
++            }
++        }, new ArrayList<AccountRuleConf>());
++    }
++
++    @Override
++    public boolean add(final ExternalResource resource) {
++        checkType(resource, JPAExternalResource.class);
++        return resources.add((JPAExternalResource) resource);
++    }
++
++    @Override
++    public boolean remove(final ExternalResource resource) {
++        checkType(resource, JPAExternalResource.class);
++        return resources.remove((JPAExternalResource) resource);
++    }
++
++    @Override
++    public Set<? extends ExternalResource> getResources() {
++        return resources;
++    }
++
++    @Override
++    public Set<String> getResourceNames() {
++        return CollectionUtils.collect(getResources(), new Transformer<ExternalResource, String>() {
++
++            @Override
++            public String transform(final ExternalResource input) {
++                return input.getKey();
++            }
++        }, new HashSet<String>());
++    }
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountRuleConfInstance.java
----------------------------------------------------------------------
diff --cc core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountRuleConfInstance.java
index 0000000,0000000..782e6b4
new file mode 100644
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountRuleConfInstance.java
@@@ -1,0 -1,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.core.persistence.jpa.entity.policy;
++
++import javax.persistence.Entity;
++import javax.persistence.Id;
++import javax.persistence.Lob;
++import javax.persistence.ManyToOne;
++import javax.persistence.Table;
++import org.apache.syncope.common.lib.policy.AccountRuleConf;
++import org.apache.syncope.core.misc.serialization.POJOHelper;
++import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
++import org.apache.syncope.core.persistence.jpa.entity.AbstractEntity;
++
++@Entity
++@Table(name = JPAAccountRuleConfInstance.TABLE)
++public class JPAAccountRuleConfInstance extends AbstractEntity<Long> {
++
++    private static final long serialVersionUID = -2436055132955674610L;
++
++    public static final String TABLE = "AccountRuleConfInstance";
++
++    @Id
++    private Long id;
++
++    @Lob
++    private String serializedInstance;
++
++    @ManyToOne
++    private JPAAccountPolicy accountPolicy;
++
++    @Override
++    public Long getKey() {
++        return id;
++    }
++
++    public AccountPolicy getAccountPolicy() {
++        return accountPolicy;
++    }
++
++    public void setAccountPolicy(final AccountPolicy report) {
++        checkType(report, JPAAccountPolicy.class);
++        this.accountPolicy = (JPAAccountPolicy) report;
++    }
++
++    public AccountRuleConf getInstance() {
++        return serializedInstance == null
++                ? null
++                : POJOHelper.deserialize(serializedInstance, AccountRuleConf.class);
++    }
++
++    public void setInstance(final AccountRuleConf instance) {
++        this.serializedInstance = instance == null
++                ? null
++                : POJOHelper.serialize(instance);
++    }
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordPolicy.java
----------------------------------------------------------------------
diff --cc core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordPolicy.java
index 0000000,0000000..1491c25
new file mode 100644
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordPolicy.java
@@@ -1,0 -1,0 +1,115 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.core.persistence.jpa.entity.policy;
++
++import java.util.ArrayList;
++import java.util.List;
++import javax.persistence.Basic;
++import javax.persistence.CascadeType;
++import javax.persistence.Entity;
++import javax.persistence.FetchType;
++import javax.persistence.OneToMany;
++import javax.persistence.Table;
++import javax.validation.constraints.Max;
++import javax.validation.constraints.Min;
++import org.apache.commons.collections4.CollectionUtils;
++import org.apache.commons.collections4.Predicate;
++import org.apache.commons.collections4.Transformer;
++import org.apache.syncope.common.lib.policy.PasswordRuleConf;
++import org.apache.syncope.common.lib.types.PolicyType;
++import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
++
++@Entity
++@Table(name = JPAPasswordPolicy.TABLE)
++public class JPAPasswordPolicy extends AbstractPolicy implements PasswordPolicy {
++
++    private static final long serialVersionUID = 9138550910385232849L;
++
++    public static final String TABLE = "PasswordPolicy";
++
++    @Basic
++    @Min(0)
++    @Max(1)
++    private Integer allowNullPassword;
++
++    private int historyLength;
++
++    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "passwordPolicy")
++    private List<JPAPasswordRuleConfInstance> ruleConfs;
++
++    public JPAPasswordPolicy() {
++        super();
++        this.type = PolicyType.PASSWORD;
++    }
++
++    @Override
++    public boolean isAllowNullPassword() {
++        return isBooleanAsInteger(allowNullPassword);
++    }
++
++    @Override
++    public void setAllowNullPassword(final boolean allowNullPassword) {
++        this.allowNullPassword = getBooleanAsInteger(allowNullPassword);
++    }
++
++    @Override
++    public int getHistoryLength() {
++        return historyLength;
++    }
++
++    @Override
++    public void setHistoryLength(final int historyLength) {
++        this.historyLength = historyLength;
++    }
++
++    @Override
++    public boolean add(final PasswordRuleConf passwordRuleConf) {
++        if (passwordRuleConf == null) {
++            return false;
++        }
++
++        JPAPasswordRuleConfInstance instance = new JPAPasswordRuleConfInstance();
++        instance.setPasswordPolicy(this);
++        instance.setInstance(passwordRuleConf);
++
++        return ruleConfs.add(instance);
++    }
++
++    @Override
++    public boolean remove(final PasswordRuleConf passwordRuleConf) {
++        return CollectionUtils.filter(ruleConfs, new Predicate<JPAPasswordRuleConfInstance>() {
++
++            @Override
++            public boolean evaluate(final JPAPasswordRuleConfInstance object) {
++                return passwordRuleConf.equals(object.getInstance());
++            }
++        });
++    }
++
++    @Override
++    public List<PasswordRuleConf> getRuleConfs() {
++        return CollectionUtils.collect(ruleConfs, new Transformer<JPAPasswordRuleConfInstance, PasswordRuleConf>() {
++
++            @Override
++            public PasswordRuleConf transform(final JPAPasswordRuleConfInstance input) {
++                return input.getInstance();
++            }
++        }, new ArrayList<PasswordRuleConf>());
++    }
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordRuleConfInstance.java
----------------------------------------------------------------------
diff --cc core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordRuleConfInstance.java
index 0000000,0000000..4b39dc8
new file mode 100644
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordRuleConfInstance.java
@@@ -1,0 -1,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.core.persistence.jpa.entity.policy;
++
++import javax.persistence.Entity;
++import javax.persistence.Id;
++import javax.persistence.Lob;
++import javax.persistence.ManyToOne;
++import javax.persistence.Table;
++import org.apache.syncope.common.lib.policy.PasswordRuleConf;
++import org.apache.syncope.core.misc.serialization.POJOHelper;
++import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
++import org.apache.syncope.core.persistence.jpa.entity.AbstractEntity;
++
++@Entity
++@Table(name = JPAPasswordRuleConfInstance.TABLE)
++public class JPAPasswordRuleConfInstance extends AbstractEntity<Long> {
++
++    private static final long serialVersionUID = -2436055132955674610L;
++
++    public static final String TABLE = "PasswordRuleConfInstance";
++
++    @Id
++    private Long id;
++
++    @Lob
++    private String serializedInstance;
++
++    @ManyToOne
++    private JPAPasswordPolicy passwordPolicy;
++
++    @Override
++    public Long getKey() {
++        return id;
++    }
++
++    public PasswordPolicy getPasswordPolicy() {
++        return passwordPolicy;
++    }
++
++    public void setPasswordPolicy(final PasswordPolicy report) {
++        checkType(report, JPAPasswordPolicy.class);
++        this.passwordPolicy = (JPAPasswordPolicy) report;
++    }
++
++    public PasswordRuleConf getInstance() {
++        return serializedInstance == null
++                ? null
++                : POJOHelper.deserialize(serializedInstance, PasswordRuleConf.class);
++    }
++
++    public void setInstance(final PasswordRuleConf instance) {
++        this.serializedInstance = instance == null
++                ? null
++                : POJOHelper.serialize(instance);
++    }
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPushPolicy.java
----------------------------------------------------------------------
diff --cc core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPushPolicy.java
index 0000000,0000000..3bc0009
new file mode 100644
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPushPolicy.java
@@@ -1,0 -1,0 +1,55 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.core.persistence.jpa.entity.policy;
++
++import javax.persistence.Entity;
++import javax.persistence.Lob;
++import javax.persistence.Table;
++import org.apache.syncope.common.lib.types.PolicyType;
++import org.apache.syncope.common.lib.policy.PushPolicySpec;
++import org.apache.syncope.core.misc.serialization.POJOHelper;
++import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy;
++
++@Entity
++@Table(name = JPAPushPolicy.TABLE)
++public class JPAPushPolicy extends AbstractPolicy implements PushPolicy {
++
++    private static final long serialVersionUID = -5875589156893921113L;
++
++    public static final String TABLE = "PushPolicy";
++
++    @Lob
++    private String specification;
++
++    public JPAPushPolicy() {
++        super();
++        this.type = PolicyType.PUSH;
++    }
++
++    @Override
++    public PushPolicySpec getSpecification() {
++        return POJOHelper.deserialize(specification, PushPolicySpec.class);
++    }
++
++    @Override
++    public void setSpecification(final PushPolicySpec policy) {
++        this.specification = POJOHelper.serialize(policy);
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPASyncPolicy.java
----------------------------------------------------------------------
diff --cc core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPASyncPolicy.java
index 0000000,0000000..913ee54
new file mode 100644
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPASyncPolicy.java
@@@ -1,0 -1,0 +1,54 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.core.persistence.jpa.entity.policy;
++
++import javax.persistence.Entity;
++import javax.persistence.Lob;
++import javax.persistence.Table;
++import org.apache.syncope.common.lib.policy.SyncPolicySpec;
++import org.apache.syncope.common.lib.types.PolicyType;
++import org.apache.syncope.core.misc.serialization.POJOHelper;
++import org.apache.syncope.core.persistence.api.entity.policy.SyncPolicy;
++
++@Entity
++@Table(name = JPASyncPolicy.TABLE)
++public class JPASyncPolicy extends AbstractPolicy implements SyncPolicy {
++
++    private static final long serialVersionUID = -6090413855809521279L;
++
++    public static final String TABLE = "SyncPolicy";
++
++    @Lob
++    private String specification;
++
++    public JPASyncPolicy() {
++        super();
++        this.type = PolicyType.SYNC;
++    }
++
++    @Override
++    public SyncPolicySpec getSpecification() {
++        return POJOHelper.deserialize(specification, SyncPolicySpec.class);
++    }
++
++    @Override
++    public void setSpecification(final SyncPolicySpec policy) {
++        this.specification = POJOHelper.serialize(policy);
++    }
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractTask.java
----------------------------------------------------------------------
diff --cc core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractTask.java
index 0000000,0000000..36c52a3
new file mode 100644
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractTask.java
@@@ -1,0 -1,0 +1,96 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.core.persistence.jpa.entity.task;
++
++import java.util.ArrayList;
++import java.util.List;
++
++import javax.persistence.CascadeType;
++import javax.persistence.DiscriminatorColumn;
++import javax.persistence.Entity;
++import javax.persistence.EnumType;
++import javax.persistence.Enumerated;
++import javax.persistence.Id;
++import javax.persistence.Inheritance;
++import javax.persistence.InheritanceType;
++import javax.persistence.OneToMany;
++import javax.persistence.Table;
++import javax.validation.constraints.NotNull;
++import org.apache.syncope.common.lib.types.TaskType;
++import org.apache.syncope.core.persistence.api.entity.task.Task;
++import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
++import org.apache.syncope.core.persistence.jpa.entity.AbstractEntity;
++
++@Entity
++@Table(name = AbstractTask.TABLE)
++@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
++@DiscriminatorColumn(name = "DTYPE")
++public abstract class AbstractTask extends AbstractEntity<Long> implements Task {
++
++    private static final long serialVersionUID = 5837401178128177511L;
++
++    public static final String TABLE = "Task";
++
++    /**
++     * Id.
++     */
++    @Id
++    private Long id;
++
++    @NotNull
++    @Enumerated(EnumType.STRING)
++    protected TaskType type;
++
++    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "task")
++    private List<JPATaskExec> executions;
++
++    public AbstractTask() {
++        super();
++
++        executions = new ArrayList<>();
++    }
++
++    @Override
++    public Long getKey() {
++        return id;
++    }
++
++    @Override
++    public TaskType getType() {
++        return type;
++    }
++
++    @Override
++    public boolean addExec(final TaskExec exec) {
++        checkType(exec, JPATaskExec.class);
++        return exec != null && !executions.contains((JPATaskExec) exec) && executions.add((JPATaskExec) exec);
++    }
++
++    @Override
++    public boolean removeExec(final TaskExec exec) {
++        checkType(exec, JPATaskExec.class);
++        return exec != null && executions.remove((JPATaskExec) exec);
++    }
++
++    @Override
++    public List<? extends TaskExec> getExecs() {
++        return executions;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestAccountRule.java
----------------------------------------------------------------------
diff --cc fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestAccountRule.java
index 0000000,0000000..2b67f37
new file mode 100644
--- /dev/null
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestAccountRule.java
@@@ -1,0 -1,0 +1,48 @@@
++/*
++ * 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.fit.core.reference;
++
++import org.apache.syncope.common.lib.policy.AccountRuleConf;
++import org.apache.syncope.core.misc.policy.AccountPolicyException;
++import org.apache.syncope.core.persistence.api.dao.AccountRule;
++import org.apache.syncope.core.persistence.api.dao.AccountRuleConfClass;
++import org.apache.syncope.core.persistence.api.entity.user.User;
++import org.springframework.transaction.annotation.Transactional;
++
++@AccountRuleConfClass(TestAccountRuleConf.class)
++public class TestAccountRule implements AccountRule {
++
++    private TestAccountRuleConf conf;
++
++    @Transactional(readOnly = true)
++    @Override
++    public void enforce(final AccountRuleConf conf, final User user) {
++        if (conf instanceof TestAccountRuleConf) {
++            this.conf = TestAccountRuleConf.class.cast(conf);
++        } else {
++            throw new IllegalArgumentException(
++                    AccountRuleConf.class.getName() + " expected, got " + conf.getClass().getName());
++        }
++
++        if (!user.getUsername().contains(this.conf.getMustContainSubstring())) {
++            throw new AccountPolicyException("Username not containing " + this.conf.getMustContainSubstring());
++        }
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestAccountRuleConf.java
----------------------------------------------------------------------
diff --cc fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestAccountRuleConf.java
index 0000000,0000000..8903058
new file mode 100644
--- /dev/null
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestAccountRuleConf.java
@@@ -1,0 -1,0 +1,36 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.fit.core.reference;
++
++import org.apache.syncope.common.lib.AbstractBaseBean;
++import org.apache.syncope.common.lib.policy.AccountRuleConf;
++
++public class TestAccountRuleConf extends AbstractBaseBean implements AccountRuleConf {
++
++    private static final long serialVersionUID = -1803947511928491978L;
++
++    @Override
++    public String getName() {
++        return getClass().getName();
++    }
++
++    public String getMustContainSubstring() {
++        return "YYY";
++    }
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPasswordRule.java
----------------------------------------------------------------------
diff --cc fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPasswordRule.java
index 0000000,0000000..2b67f37
new file mode 100644
--- /dev/null
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPasswordRule.java
@@@ -1,0 -1,0 +1,48 @@@
++/*
++ * 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.fit.core.reference;
++
++import org.apache.syncope.common.lib.policy.AccountRuleConf;
++import org.apache.syncope.core.misc.policy.AccountPolicyException;
++import org.apache.syncope.core.persistence.api.dao.AccountRule;
++import org.apache.syncope.core.persistence.api.dao.AccountRuleConfClass;
++import org.apache.syncope.core.persistence.api.entity.user.User;
++import org.springframework.transaction.annotation.Transactional;
++
++@AccountRuleConfClass(TestAccountRuleConf.class)
++public class TestAccountRule implements AccountRule {
++
++    private TestAccountRuleConf conf;
++
++    @Transactional(readOnly = true)
++    @Override
++    public void enforce(final AccountRuleConf conf, final User user) {
++        if (conf instanceof TestAccountRuleConf) {
++            this.conf = TestAccountRuleConf.class.cast(conf);
++        } else {
++            throw new IllegalArgumentException(
++                    AccountRuleConf.class.getName() + " expected, got " + conf.getClass().getName());
++        }
++
++        if (!user.getUsername().contains(this.conf.getMustContainSubstring())) {
++            throw new AccountPolicyException("Username not containing " + this.conf.getMustContainSubstring());
++        }
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/syncope/blob/77f697b2/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPasswordRuleConf.java
----------------------------------------------------------------------
diff --cc fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPasswordRuleConf.java
index 0000000,0000000..8903058
new file mode 100644
--- /dev/null
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPasswordRuleConf.java
@@@ -1,0 -1,0 +1,36 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements.  See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership.  The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License.  You may obtain a copy of the License at
++ *
++ *   http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing,
++ * software distributed under the License is distributed on an
++ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ * KIND, either express or implied.  See the License for the
++ * specific language governing permissions and limitations
++ * under the License.
++ */
++package org.apache.syncope.fit.core.reference;
++
++import org.apache.syncope.common.lib.AbstractBaseBean;
++import org.apache.syncope.common.lib.policy.AccountRuleConf;
++
++public class TestAccountRuleConf extends AbstractBaseBean implements AccountRuleConf {
++
++    private static final long serialVersionUID = -1803947511928491978L;
++
++    @Override
++    public String getName() {
++        return getClass().getName();
++    }
++
++    public String getMustContainSubstring() {
++        return "YYY";
++    }
++}


[15/33] syncope git commit: [SYNCOPE-685] Now account and password policies can be composed of several 'rules', where each rule can be provided as a separate Java class (similar to Reportlet mechanism); previous account and password policies are now impl

Posted by md...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java
index f7a8def..d790568 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java
@@ -20,7 +20,10 @@ package org.apache.syncope.core.logic.report;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.lang.reflect.Modifier;
 import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.zip.Deflater;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
@@ -30,10 +33,10 @@ import javax.xml.transform.sax.SAXTransformerFactory;
 import javax.xml.transform.sax.TransformerHandler;
 import javax.xml.transform.stream.StreamResult;
 import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.common.lib.types.ReportExecStatus;
-import org.apache.syncope.core.logic.ReportLogic;
 import org.apache.syncope.core.misc.ExceptionUtils2;
 import org.apache.syncope.core.misc.spring.ApplicationContextProvider;
 import org.apache.syncope.core.persistence.api.dao.ReportDAO;
@@ -45,9 +48,13 @@ import org.quartz.JobExecutionException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
+import org.springframework.core.type.filter.AssignableTypeFilter;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.ClassUtils;
 import org.xml.sax.helpers.AttributesImpl;
 
 @Component
@@ -55,6 +62,32 @@ public class ReportJobDelegate {
 
     private static final Logger LOG = LoggerFactory.getLogger(ReportJobDelegate.class);
 
+    private static final Map<Class<? extends ReportletConf>, Class<Reportlet>> REPORTLET_CLASSES = new HashMap<>();
+
+    static {
+        initReportletClasses();
+    }
+
+    @SuppressWarnings("unchecked")
+    private static void initReportletClasses() {
+        ClassPathScanningCandidateComponentProvider scanner =
+                new ClassPathScanningCandidateComponentProvider(false);
+        scanner.addIncludeFilter(new AssignableTypeFilter(Reportlet.class));
+
+        for (BeanDefinition bd : scanner.findCandidateComponents(StringUtils.EMPTY)) {
+            Class<?> clazz = ClassUtils.resolveClassName(
+                    bd.getBeanClassName(), ClassUtils.getDefaultClassLoader());
+            boolean isAbstract = Modifier.isAbstract(clazz.getModifiers());
+
+            if (Reportlet.class.isAssignableFrom(clazz) && !isAbstract) {
+                ReportletConfClass annotation = clazz.getAnnotation(ReportletConfClass.class);
+                if (annotation != null) {
+                    REPORTLET_CLASSES.put(annotation.value(), (Class<Reportlet>) clazz);
+                }
+            }
+        }
+    }
+
     /**
      * Report DAO.
      */
@@ -70,9 +103,6 @@ public class ReportJobDelegate {
     @Autowired
     private EntityFactory entityFactory;
 
-    @Autowired
-    private ReportLogic dataBinder;
-
     @Transactional
     public void execute(final Long reportKey) throws JobExecutionException {
         Report report = reportDAO.find(reportKey);
@@ -126,18 +156,25 @@ public class ReportJobDelegate {
 
             // iterate over reportlet instances defined for this report
             for (ReportletConf reportletConf : report.getReportletConfs()) {
-                Class<Reportlet> reportletClass =
-                        dataBinder.findReportletClassHavingConfClass(reportletConf.getClass());
-                if (reportletClass != null) {
-                    @SuppressWarnings("unchecked")
-                    Reportlet<ReportletConf> autowired =
-                            (Reportlet<ReportletConf>) ApplicationContextProvider.getBeanFactory().
-                            createBean(reportletClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
-                    autowired.setConf(reportletConf);
+                Class<Reportlet> reportletClass = REPORTLET_CLASSES.get(reportletConf.getClass());
+                if (reportletClass == null) {
+                    LOG.warn("Could not find matching reportlet for {}", reportletConf.getClass());
+                } else {
+                    // fetch (or create) reportlet
+                    Reportlet reportlet;
+                    if (ApplicationContextProvider.getBeanFactory().containsSingleton(reportletClass.getName())) {
+                        reportlet = (Reportlet) ApplicationContextProvider.getBeanFactory().
+                                getSingleton(reportletClass.getName());
+                    } else {
+                        reportlet = (Reportlet) ApplicationContextProvider.getBeanFactory().
+                                createBean(reportletClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
+                        ApplicationContextProvider.getBeanFactory().
+                                registerSingleton(reportletClass.getName(), reportlet);
+                    }
 
                     // invoke reportlet
                     try {
-                        autowired.extract(handler);
+                        reportlet.extract(reportletConf, handler);
                     } catch (Exception e) {
                         execution.setStatus(ReportExecStatus.FAILURE);
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/logic/src/main/java/org/apache/syncope/core/logic/report/Reportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/Reportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/Reportlet.java
index 65fab56..66d00c4 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/Reportlet.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/Reportlet.java
@@ -25,22 +25,16 @@ import org.xml.sax.SAXException;
 /**
  * Interface for all elements that can be embedded in a report.
  *
- * @see org.apache.syncope.core.persistence.beans.Report
+ * @see org.apache.syncope.core.persistence.api.entity.Report
  */
-public interface Reportlet<T extends ReportletConf> {
-
-    /**
-     * Set this reportlet configuration.
-     *
-     * @param conf configuration
-     */
-    void setConf(T conf);
+public interface Reportlet {
 
     /**
      * Actual data extraction for reporting.
      *
+     * @param conf configuration
      * @param handler SAX content handler for streaming result
      * @throws SAXException if there is any problem in SAX handling
      */
-    void extract(ContentHandler handler) throws SAXException;
+    void extract(ReportletConf conf, ContentHandler handler) throws SAXException;
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java
index 3e768e7..963d484 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.logic.report;
 
+import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.common.lib.report.StaticReportletConf;
 import org.apache.syncope.core.misc.DataFormat;
 import org.springframework.util.StringUtils;
@@ -26,10 +27,11 @@ import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
 
 @ReportletConfClass(StaticReportletConf.class)
-public class StaticReportlet extends AbstractReportlet<StaticReportletConf> {
+public class StaticReportlet extends AbstractReportlet {
 
-    private void doExtractConf(final ContentHandler handler) throws SAXException {
+    private StaticReportletConf conf;
 
+    private void doExtractConf(final ContentHandler handler) throws SAXException {
         AttributesImpl atts = new AttributesImpl();
         handler.startElement("", "", "configurations", null);
         handler.startElement("", "", "staticAttributes", atts);
@@ -67,46 +69,52 @@ public class StaticReportlet extends AbstractReportlet<StaticReportletConf> {
     }
 
     @Override
-    public void doExtract(final ContentHandler handler) throws SAXException {
+    protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+        if (conf instanceof StaticReportletConf) {
+            this.conf = StaticReportletConf.class.cast(conf);
+        } else {
+            throw new ReportException(new IllegalArgumentException("Invalid configuration provided"));
+        }
+
         doExtractConf(handler);
 
-        if (StringUtils.hasText(conf.getStringField())) {
+        if (StringUtils.hasText(this.conf.getStringField())) {
             handler.startElement("", "", "string", null);
-            handler.characters(conf.getStringField().toCharArray(), 0, conf.getStringField().length());
+            handler.characters(this.conf.getStringField().toCharArray(), 0, this.conf.getStringField().length());
             handler.endElement("", "", "string");
         }
 
-        if (conf.getLongField() != null) {
+        if (this.conf.getLongField() != null) {
             handler.startElement("", "", "long", null);
-            String printed = String.valueOf(conf.getLongField());
+            String printed = String.valueOf(this.conf.getLongField());
             handler.characters(printed.toCharArray(), 0, printed.length());
             handler.endElement("", "", "long");
         }
 
-        if (conf.getDoubleField() != null) {
+        if (this.conf.getDoubleField() != null) {
             handler.startElement("", "", "double", null);
-            String printed = String.valueOf(conf.getDoubleField());
+            String printed = String.valueOf(this.conf.getDoubleField());
             handler.characters(printed.toCharArray(), 0, printed.length());
             handler.endElement("", "", "double");
         }
 
-        if (conf.getDateField() != null) {
+        if (this.conf.getDateField() != null) {
             handler.startElement("", "", "date", null);
-            String printed = DataFormat.format(conf.getDateField());
+            String printed = DataFormat.format(this.conf.getDateField());
             handler.characters(printed.toCharArray(), 0, printed.length());
             handler.endElement("", "", "date");
         }
 
-        if (conf.getTraceLevel() != null) {
+        if (this.conf.getTraceLevel() != null) {
             handler.startElement("", "", "enum", null);
-            String printed = conf.getTraceLevel().name();
+            String printed = this.conf.getTraceLevel().name();
             handler.characters(printed.toCharArray(), 0, printed.length());
             handler.endElement("", "", "enum");
         }
 
-        if (conf.getListField() != null && !conf.getListField().isEmpty()) {
+        if (this.conf.getListField() != null && !this.conf.getListField().isEmpty()) {
             handler.startElement("", "", "list", null);
-            for (String item : conf.getListField()) {
+            for (String item : this.conf.getListField()) {
                 if (StringUtils.hasText(item)) {
                     handler.startElement("", "", "string", null);
                     handler.characters(item.toCharArray(), 0, item.length());

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
index b626dd1..44ae0e0 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
@@ -24,6 +24,7 @@ import java.util.List;
 import java.util.Map;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.common.lib.report.UserReportletConf;
 import org.apache.syncope.common.lib.report.UserReportletConf.Feature;
 import org.apache.syncope.common.lib.to.AnyTO;
@@ -49,7 +50,7 @@ import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
 
 @ReportletConfClass(UserReportletConf.class)
-public class UserReportlet extends AbstractReportlet<UserReportletConf> {
+public class UserReportlet extends AbstractReportlet {
 
     private static final int PAGE_SIZE = 10;
 
@@ -68,6 +69,8 @@ public class UserReportlet extends AbstractReportlet<UserReportletConf> {
     @Autowired
     private AnyObjectDataBinder anyObjectDataBinder;
 
+    private UserReportletConf conf;
+
     private List<User> getPagedUsers(final int page) {
         List<User> result;
 
@@ -370,7 +373,13 @@ public class UserReportlet extends AbstractReportlet<UserReportletConf> {
     }
 
     @Override
-    protected void doExtract(final ContentHandler handler) throws SAXException {
+    protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+        if (conf instanceof UserReportletConf) {
+            this.conf = UserReportletConf.class.cast(conf);
+        } else {
+            throw new ReportException(new IllegalArgumentException("Invalid configuration provided"));
+        }
+
         doExtractConf(handler);
         for (int i = 1; i <= (count() / PAGE_SIZE) + 1; i++) {
             doExtract(handler, getPagedUsers(i));

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/misc/src/main/java/org/apache/syncope/core/misc/ConnObjectUtils.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/ConnObjectUtils.java b/core/misc/src/main/java/org/apache/syncope/core/misc/ConnObjectUtils.java
index de1bcfb..5ab5f44 100644
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/ConnObjectUtils.java
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/ConnObjectUtils.java
@@ -18,8 +18,7 @@
  */
 package org.apache.syncope.core.misc;
 
-import org.apache.syncope.core.misc.policy.InvalidPasswordPolicySpecException;
-import org.apache.syncope.core.misc.security.PasswordGenerator;
+import org.apache.syncope.core.misc.policy.InvalidPasswordRuleConf;
 import org.apache.syncope.core.misc.security.SecureRandomUtils;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -30,6 +29,7 @@ import java.util.Map;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.AnyOperations;
 import org.apache.syncope.common.lib.mod.AnyMod;
+import org.apache.syncope.common.lib.policy.PasswordRuleConf;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.to.AttrTO;
@@ -41,7 +41,6 @@ import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.AttrSchemaType;
 import org.apache.syncope.common.lib.types.MappingPurpose;
-import org.apache.syncope.common.lib.types.PasswordPolicySpec;
 import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
 import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
@@ -57,6 +56,7 @@ import org.apache.syncope.core.persistence.api.entity.task.SyncTask;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.misc.security.Encryptor;
 import org.apache.syncope.core.misc.jexl.JexlUtils;
+import org.apache.syncope.core.misc.security.PasswordGenerator;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.resource.Provision;
@@ -117,32 +117,28 @@ public class ConnObjectUtils {
         if (anyTO instanceof UserTO && StringUtils.isBlank(((UserTO) anyTO).getPassword())) {
             final UserTO userTO = (UserTO) anyTO;
 
-            List<PasswordPolicySpec> ppSpecs = new ArrayList<>();
+            List<PasswordRuleConf> ruleConfs = new ArrayList<>();
 
             Realm realm = realmDAO.find(userTO.getRealm());
             if (realm != null) {
                 for (Realm ancestor : realmDAO.findAncestors(realm)) {
-                    if (ancestor.getPasswordPolicy() != null
-                            && ancestor.getPasswordPolicy().getSpecification() != null) {
-
-                        ppSpecs.add(ancestor.getPasswordPolicy().getSpecification());
+                    if (ancestor.getPasswordPolicy() != null) {
+                        ruleConfs.addAll(ancestor.getPasswordPolicy().getRuleConfs());
                     }
                 }
             }
 
             for (String resName : userTO.getResources()) {
                 ExternalResource resource = resourceDAO.find(resName);
-                if (resource != null && resource.getPasswordPolicy() != null
-                        && resource.getPasswordPolicy().getSpecification() != null) {
-
-                    ppSpecs.add(resource.getPasswordPolicy().getSpecification());
+                if (resource != null && resource.getPasswordPolicy() != null) {
+                    ruleConfs.addAll(resource.getPasswordPolicy().getRuleConfs());
                 }
             }
 
             String password;
             try {
-                password = pwdGen.generate(ppSpecs);
-            } catch (InvalidPasswordPolicySpecException e) {
+                password = pwdGen.generate(ruleConfs);
+            } catch (InvalidPasswordRuleConf e) {
                 LOG.error("Could not generate policy-compliant random password for {}", userTO, e);
 
                 password = SecureRandomUtils.generateRandomPassword(16);

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/misc/src/main/java/org/apache/syncope/core/misc/MappingUtils.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/MappingUtils.java b/core/misc/src/main/java/org/apache/syncope/core/misc/MappingUtils.java
index 95a16b1..5cb419d 100644
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/MappingUtils.java
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/MappingUtils.java
@@ -18,8 +18,7 @@
  */
 package org.apache.syncope.core.misc;
 
-import org.apache.syncope.core.misc.policy.InvalidPasswordPolicySpecException;
-import org.apache.syncope.core.misc.security.PasswordGenerator;
+import org.apache.syncope.core.misc.policy.InvalidPasswordRuleConf;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -58,6 +57,7 @@ import org.apache.syncope.core.provisioning.api.cache.VirAttrCache;
 import org.apache.syncope.core.misc.security.Encryptor;
 import org.apache.syncope.core.misc.spring.ApplicationContextProvider;
 import org.apache.syncope.core.misc.jexl.JexlUtils;
+import org.apache.syncope.core.misc.security.PasswordGenerator;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.entity.Any;
@@ -329,7 +329,7 @@ public final class MappingUtils {
                     } else if (provision.getResource().isRandomPwdIfNotProvided()) {
                         try {
                             passwordAttrValue = passwordGenerator.generate(user);
-                        } catch (InvalidPasswordPolicySpecException e) {
+                        } catch (InvalidPasswordRuleConf e) {
                             LOG.error("Could not generate policy-compliant random password for {}", user, e);
                         }
                     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/misc/src/main/java/org/apache/syncope/core/misc/policy/AccountPolicyEnforcer.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/policy/AccountPolicyEnforcer.java b/core/misc/src/main/java/org/apache/syncope/core/misc/policy/AccountPolicyEnforcer.java
deleted file mode 100644
index 8b7a547..0000000
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/policy/AccountPolicyEnforcer.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.misc.policy;
-
-import java.util.regex.Pattern;
-import org.apache.syncope.common.lib.types.AccountPolicySpec;
-import org.apache.syncope.core.persistence.api.entity.user.User;
-import org.springframework.stereotype.Component;
-
-@Component
-public class AccountPolicyEnforcer {
-
-    private static final Pattern DEFAULT_PATTERN = Pattern.compile("[a-zA-Z0-9-_@. ]+");
-
-    public boolean enforce(final AccountPolicySpec policy, final User user) {
-        if (user.getUsername() == null) {
-            throw new AccountPolicyException("Invalid account");
-        }
-
-        // check min length
-        if (policy.getMinLength() > 0 && policy.getMinLength() > user.getUsername().length()) {
-            throw new AccountPolicyException("Username too short");
-        }
-
-        // check max length
-        if (policy.getMaxLength() > 0 && policy.getMaxLength() < user.getUsername().length()) {
-            throw new AccountPolicyException("Username too long");
-        }
-
-        // check words not permitted
-        for (String word : policy.getWordsNotPermitted()) {
-            if (user.getUsername().contains(word)) {
-                throw new AccountPolicyException("Used word(s) not permitted");
-            }
-        }
-
-        // check case
-        if (policy.isAllUpperCase() && !user.getUsername().equals(user.getUsername().toUpperCase())) {
-            throw new AccountPolicyException("No lowercase characters permitted");
-        }
-        if (policy.isAllLowerCase() && !user.getUsername().equals(user.getUsername().toLowerCase())) {
-            throw new AccountPolicyException("No uppercase characters permitted");
-        }
-
-        // check pattern
-        Pattern pattern = (policy.getPattern() == null) ? DEFAULT_PATTERN : Pattern.compile(policy.getPattern());
-        if (!pattern.matcher(user.getUsername()).matches()) {
-            throw new AccountPolicyException("Username does not match pattern");
-        }
-
-        // check prefix
-        for (String prefix : policy.getPrefixesNotPermitted()) {
-            if (user.getUsername().startsWith(prefix)) {
-                throw new AccountPolicyException("Prefix not permitted");
-            }
-        }
-
-        // check suffix
-        for (String suffix : policy.getSuffixesNotPermitted()) {
-            if (user.getUsername().endsWith(suffix)) {
-                throw new AccountPolicyException("Suffix not permitted");
-            }
-        }
-
-        // check for subsequent failed logins
-        return (user.getFailedLogins() != null && policy.getMaxAuthenticationAttempts() > 0
-                && user.getFailedLogins() > policy.getMaxAuthenticationAttempts() && !user.isSuspended());
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/misc/src/main/java/org/apache/syncope/core/misc/policy/InvalidPasswordPolicySpecException.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/policy/InvalidPasswordPolicySpecException.java b/core/misc/src/main/java/org/apache/syncope/core/misc/policy/InvalidPasswordPolicySpecException.java
deleted file mode 100644
index 7d50951..0000000
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/policy/InvalidPasswordPolicySpecException.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.misc.policy;
-
-/**
- * Raise when the merge of two or more PasswordPolicySpec leds to incompatible condition.
- *
- * @see org.apache.syncope.common.lib.types.PasswordPolicySpec
- */
-public class InvalidPasswordPolicySpecException extends Exception {
-
-    private static final long serialVersionUID = 4810651743226663580L;
-
-    public InvalidPasswordPolicySpecException(final String msg) {
-        super(msg);
-    }
-
-    public InvalidPasswordPolicySpecException(final String msg, final Exception e) {
-        super(msg, e);
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PasswordPolicyEnforcer.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PasswordPolicyEnforcer.java b/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PasswordPolicyEnforcer.java
deleted file mode 100644
index c2af367..0000000
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PasswordPolicyEnforcer.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.misc.policy;
-
-import org.apache.syncope.common.lib.types.PasswordPolicySpec;
-import org.apache.syncope.core.persistence.api.entity.user.User;
-import org.springframework.stereotype.Component;
-
-@Component
-public class PasswordPolicyEnforcer {
-
-    public boolean enforce(final PasswordPolicySpec policy, final User user) {
-        String clearPassword = user.getClearPassword();
-        String password = user.getPassword();
-
-        if (password == null && !policy.isAllowNullPassword()) {
-            throw new PasswordPolicyException("Password mandatory");
-        }
-        
-        if (password != null && clearPassword != null) {
-            // check length
-            if (policy.getMinLength() > 0 && policy.getMinLength() > clearPassword.length()) {
-                throw new PasswordPolicyException("Password too short");
-            }
-
-            if (policy.getMaxLength() > 0 && policy.getMaxLength() < clearPassword.length()) {
-                throw new PasswordPolicyException("Password too long");
-            }
-
-            // check words not permitted
-            for (String word : policy.getWordsNotPermitted()) {
-                if (clearPassword.contains(word)) {
-                    throw new PasswordPolicyException("Used word(s) not permitted");
-                }
-            }
-
-            // check digits occurrence
-            if (policy.isDigitRequired() && !checkForDigit(clearPassword)) {
-                throw new PasswordPolicyException("Password must contain digit(s)");
-            }
-
-            // check lowercase alphabetic characters occurrence
-            if (policy.isLowercaseRequired() && !checkForLowercase(clearPassword)) {
-                throw new PasswordPolicyException("Password must contain lowercase alphabetic character(s)");
-            }
-
-            // check uppercase alphabetic characters occurrence
-            if (policy.isUppercaseRequired() && !checkForUppercase(clearPassword)) {
-                throw new PasswordPolicyException("Password must contain uppercase alphabetic character(s)");
-            }
-
-            // check prefix
-            for (String prefix : policy.getPrefixesNotPermitted()) {
-                if (clearPassword.startsWith(prefix)) {
-                    throw new PasswordPolicyException("Prefix not permitted");
-                }
-            }
-
-            // check suffix
-            for (String suffix : policy.getSuffixesNotPermitted()) {
-                if (clearPassword.endsWith(suffix)) {
-                    throw new PasswordPolicyException("Suffix not permitted");
-                }
-            }
-
-            // check digit first occurrence
-            if (policy.isMustStartWithDigit() && !checkForFirstDigit(clearPassword)) {
-                throw new PasswordPolicyException("Password must start with a digit");
-            }
-
-            if (policy.isMustntStartWithDigit() && checkForFirstDigit(clearPassword)) {
-                throw new PasswordPolicyException("Password mustn't start with a digit");
-            }
-
-            // check digit last occurrence
-            if (policy.isMustEndWithDigit() && !checkForLastDigit(clearPassword)) {
-                throw new PasswordPolicyException("Password must end with a digit");
-            }
-
-            if (policy.isMustntEndWithDigit() && checkForLastDigit(clearPassword)) {
-                throw new PasswordPolicyException("Password mustn't end with a digit");
-            }
-
-            // check alphanumeric characters occurence
-            if (policy.isAlphanumericRequired() && !checkForAlphanumeric(clearPassword)) {
-                throw new PasswordPolicyException("Password must contain alphanumeric character(s)");
-            }
-
-            // check non alphanumeric characters occurence
-            if (policy.isNonAlphanumericRequired() && !checkForNonAlphanumeric(clearPassword)) {
-                throw new PasswordPolicyException("Password must contain non-alphanumeric character(s)");
-            }
-
-            // check alphanumeric character first occurrence
-            if (policy.isMustStartWithAlpha() && !checkForFirstAlphanumeric(clearPassword)) {
-                throw new PasswordPolicyException("Password must start with an alphanumeric character");
-            }
-
-            if (policy.isMustntStartWithAlpha() && checkForFirstAlphanumeric(clearPassword)) {
-                throw new PasswordPolicyException("Password mustn't start with an alphanumeric character");
-            }
-
-            // check alphanumeric character last occurrence
-            if (policy.isMustEndWithAlpha() && !checkForLastAlphanumeric(clearPassword)) {
-                throw new PasswordPolicyException("Password must end with an alphanumeric character");
-            }
-
-            if (policy.isMustntEndWithAlpha() && checkForLastAlphanumeric(clearPassword)) {
-                throw new PasswordPolicyException("Password mustn't end with an alphanumeric character");
-            }
-
-            // check non alphanumeric character first occurrence
-            if (policy.isMustStartWithNonAlpha() && !checkForFirstNonAlphanumeric(clearPassword)) {
-                throw new PasswordPolicyException("Password must start with a non-alphanumeric character");
-            }
-
-            if (policy.isMustntStartWithNonAlpha() && checkForFirstNonAlphanumeric(clearPassword)) {
-                throw new PasswordPolicyException("Password mustn't start with a non-alphanumeric character");
-            }
-
-            // check non alphanumeric character last occurrence
-            if (policy.isMustEndWithNonAlpha() && !checkForLastNonAlphanumeric(clearPassword)) {
-                throw new PasswordPolicyException("Password must end with a non-alphanumeric character");
-            }
-
-            if (policy.isMustntEndWithNonAlpha() && checkForLastNonAlphanumeric(clearPassword)) {
-                throw new PasswordPolicyException("Password mustn't end with a non-alphanumeric character");
-            }
-        }
-
-        return false;
-    }
-
-    private boolean checkForDigit(final String str) {
-        return PolicyPattern.DIGIT.matcher(str).matches();
-    }
-
-    private boolean checkForLowercase(final String str) {
-        return PolicyPattern.ALPHA_LOWERCASE.matcher(str).matches();
-    }
-
-    private boolean checkForUppercase(final String str) {
-        return PolicyPattern.ALPHA_UPPERCASE.matcher(str).matches();
-    }
-
-    private boolean checkForFirstDigit(final String str) {
-        return PolicyPattern.FIRST_DIGIT.matcher(str).matches();
-    }
-
-    private boolean checkForLastDigit(final String str) {
-        return PolicyPattern.LAST_DIGIT.matcher(str).matches();
-    }
-
-    private boolean checkForAlphanumeric(final String str) {
-        return PolicyPattern.ALPHANUMERIC.matcher(str).matches();
-    }
-
-    private boolean checkForFirstAlphanumeric(final String str) {
-        return PolicyPattern.FIRST_ALPHANUMERIC.matcher(str).matches();
-    }
-
-    private boolean checkForLastAlphanumeric(final String str) {
-        return PolicyPattern.LAST_ALPHANUMERIC.matcher(str).matches();
-    }
-
-    private boolean checkForNonAlphanumeric(final String str) {
-        return PolicyPattern.NON_ALPHANUMERIC.matcher(str).matches();
-    }
-
-    private boolean checkForFirstNonAlphanumeric(final String str) {
-        return PolicyPattern.FIRST_NON_ALPHANUMERIC.matcher(str).matches();
-    }
-
-    private boolean checkForLastNonAlphanumeric(final String str) {
-        return PolicyPattern.LAST_NON_ALPHANUMERIC.matcher(str).matches();
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PolicyEvaluator.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PolicyEvaluator.java b/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PolicyEvaluator.java
deleted file mode 100644
index 48cc981..0000000
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PolicyEvaluator.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.misc.policy;
-
-import java.util.List;
-import org.apache.syncope.common.lib.types.AbstractPolicySpec;
-import org.apache.syncope.common.lib.types.AccountPolicySpec;
-import org.apache.syncope.common.lib.types.PasswordPolicySpec;
-import org.apache.syncope.core.persistence.api.entity.AccountPolicy;
-import org.apache.syncope.core.persistence.api.entity.Any;
-import org.apache.syncope.core.persistence.api.entity.PasswordPolicy;
-import org.apache.syncope.core.persistence.api.entity.PlainAttr;
-import org.apache.syncope.core.persistence.api.entity.Policy;
-import org.apache.syncope.core.persistence.api.entity.user.User;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.BeanUtils;
-import org.springframework.stereotype.Component;
-
-@Component
-public class PolicyEvaluator {
-
-    private static final Logger LOG = LoggerFactory.getLogger(PolicyEvaluator.class);
-
-    @SuppressWarnings("unchecked")
-    public <T extends AbstractPolicySpec> T evaluate(final Policy policy, final Any<?, ?, ?> any) {
-        T result;
-        switch (policy.getType()) {
-            case PASSWORD:
-                PasswordPolicySpec ppSpec = ((PasswordPolicy) policy).getSpecification();
-                PasswordPolicySpec evaluatedPPSpec = new PasswordPolicySpec();
-
-                BeanUtils.copyProperties(ppSpec, evaluatedPPSpec, new String[] { "schemasNotPermitted" });
-
-                for (String schema : ppSpec.getSchemasNotPermitted()) {
-                    PlainAttr<?> attr = any.getPlainAttr(schema);
-                    if (attr != null) {
-                        List<String> values = attr.getValuesAsStrings();
-                        if (values != null && !values.isEmpty()) {
-                            evaluatedPPSpec.getWordsNotPermitted().add(values.get(0));
-                        }
-                    }
-                }
-
-                // Password history verification and update
-                if (!(any instanceof User)) {
-                    LOG.error("Cannot check previous passwords. instance is not user object: {}",
-                            any.getClass().getName());
-                    result = (T) evaluatedPPSpec;
-                    break;
-                }
-                User user = (User) any;
-                if (user.verifyPasswordHistory(user.getClearPassword(), ppSpec.getHistoryLength())) {
-                    evaluatedPPSpec.getWordsNotPermitted().add(user.getClearPassword());
-                }
-                result = (T) evaluatedPPSpec;
-                break;
-
-            case ACCOUNT:
-                AccountPolicySpec spec = ((AccountPolicy) policy).getSpecification();
-                AccountPolicySpec accountPolicy = new AccountPolicySpec();
-
-                BeanUtils.copyProperties(spec, accountPolicy, new String[] { "schemasNotPermitted" });
-
-                for (String schema : spec.getSchemasNotPermitted()) {
-                    PlainAttr<?> attr = any.getPlainAttr(schema);
-                    if (attr != null) {
-                        List<String> values = attr.getValuesAsStrings();
-                        if (values != null && !values.isEmpty()) {
-                            accountPolicy.getWordsNotPermitted().add(values.get(0));
-                        }
-                    }
-                }
-
-                result = (T) accountPolicy;
-                break;
-
-            default:
-                result = null;
-        }
-
-        return result;
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/misc/src/main/java/org/apache/syncope/core/misc/security/DefaultPasswordGenerator.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/security/DefaultPasswordGenerator.java b/core/misc/src/main/java/org/apache/syncope/core/misc/security/DefaultPasswordGenerator.java
index a1a1822..9652f73 100644
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/security/DefaultPasswordGenerator.java
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/security/DefaultPasswordGenerator.java
@@ -88,81 +88,85 @@ public class DefaultPasswordGenerator implements PasswordGenerator {
     }
 
     private DefaultPasswordRuleConf merge(final List<DefaultPasswordRuleConf> defaultRuleConfs) {
-        DefaultPasswordRuleConf fpps = new DefaultPasswordRuleConf();
-        fpps.setMinLength(VERY_MIN_LENGTH);
-        fpps.setMaxLength(VERY_MAX_LENGTH);
+        DefaultPasswordRuleConf result = new DefaultPasswordRuleConf();
+        result.setMinLength(VERY_MIN_LENGTH);
+        result.setMaxLength(VERY_MAX_LENGTH);
 
         for (DefaultPasswordRuleConf ruleConf : defaultRuleConfs) {
-            if (ruleConf.getMinLength() > fpps.getMinLength()) {
-                fpps.setMinLength(ruleConf.getMinLength());
+            if (ruleConf.getMinLength() > result.getMinLength()) {
+                result.setMinLength(ruleConf.getMinLength());
             }
 
-            if ((ruleConf.getMaxLength() != 0) && ((ruleConf.getMaxLength() < fpps.getMaxLength()))) {
-                fpps.setMaxLength(ruleConf.getMaxLength());
+            if ((ruleConf.getMaxLength() != 0) && ((ruleConf.getMaxLength() < result.getMaxLength()))) {
+                result.setMaxLength(ruleConf.getMaxLength());
             }
-            fpps.getPrefixesNotPermitted().addAll(ruleConf.getPrefixesNotPermitted());
-            fpps.getSuffixesNotPermitted().addAll(ruleConf.getSuffixesNotPermitted());
+            result.getPrefixesNotPermitted().addAll(ruleConf.getPrefixesNotPermitted());
+            result.getSuffixesNotPermitted().addAll(ruleConf.getSuffixesNotPermitted());
 
-            if (!fpps.isNonAlphanumericRequired()) {
-                fpps.setNonAlphanumericRequired(ruleConf.isNonAlphanumericRequired());
+            if (!result.isNonAlphanumericRequired()) {
+                result.setNonAlphanumericRequired(ruleConf.isNonAlphanumericRequired());
             }
 
-            if (!fpps.isAlphanumericRequired()) {
-                fpps.setAlphanumericRequired(ruleConf.isAlphanumericRequired());
+            if (!result.isAlphanumericRequired()) {
+                result.setAlphanumericRequired(ruleConf.isAlphanumericRequired());
             }
-            if (!fpps.isDigitRequired()) {
-                fpps.setDigitRequired(ruleConf.isDigitRequired());
+            if (!result.isDigitRequired()) {
+                result.setDigitRequired(ruleConf.isDigitRequired());
             }
 
-            if (!fpps.isLowercaseRequired()) {
-                fpps.setLowercaseRequired(ruleConf.isLowercaseRequired());
+            if (!result.isLowercaseRequired()) {
+                result.setLowercaseRequired(ruleConf.isLowercaseRequired());
             }
-            if (!fpps.isUppercaseRequired()) {
-                fpps.setUppercaseRequired(ruleConf.isUppercaseRequired());
+            if (!result.isUppercaseRequired()) {
+                result.setUppercaseRequired(ruleConf.isUppercaseRequired());
             }
-            if (!fpps.isMustStartWithDigit()) {
-                fpps.setMustStartWithDigit(ruleConf.isMustStartWithDigit());
+            if (!result.isMustStartWithDigit()) {
+                result.setMustStartWithDigit(ruleConf.isMustStartWithDigit());
             }
-            if (!fpps.isMustntStartWithDigit()) {
-                fpps.setMustntStartWithDigit(ruleConf.isMustntStartWithDigit());
+            if (!result.isMustntStartWithDigit()) {
+                result.setMustntStartWithDigit(ruleConf.isMustntStartWithDigit());
             }
-            if (!fpps.isMustEndWithDigit()) {
-                fpps.setMustEndWithDigit(ruleConf.isMustEndWithDigit());
+            if (!result.isMustEndWithDigit()) {
+                result.setMustEndWithDigit(ruleConf.isMustEndWithDigit());
             }
-            if (fpps.isMustntEndWithDigit()) {
-                fpps.setMustntEndWithDigit(ruleConf.isMustntEndWithDigit());
+            if (result.isMustntEndWithDigit()) {
+                result.setMustntEndWithDigit(ruleConf.isMustntEndWithDigit());
             }
-            if (!fpps.isMustStartWithAlpha()) {
-                fpps.setMustStartWithAlpha(ruleConf.isMustStartWithAlpha());
+            if (!result.isMustStartWithAlpha()) {
+                result.setMustStartWithAlpha(ruleConf.isMustStartWithAlpha());
             }
-            if (!fpps.isMustntStartWithAlpha()) {
-                fpps.setMustntStartWithAlpha(ruleConf.isMustntStartWithAlpha());
+            if (!result.isMustntStartWithAlpha()) {
+                result.setMustntStartWithAlpha(ruleConf.isMustntStartWithAlpha());
             }
-            if (!fpps.isMustStartWithNonAlpha()) {
-                fpps.setMustStartWithNonAlpha(ruleConf.isMustStartWithNonAlpha());
+            if (!result.isMustStartWithNonAlpha()) {
+                result.setMustStartWithNonAlpha(ruleConf.isMustStartWithNonAlpha());
             }
-            if (!fpps.isMustntStartWithNonAlpha()) {
-                fpps.setMustntStartWithNonAlpha(ruleConf.isMustntStartWithNonAlpha());
+            if (!result.isMustntStartWithNonAlpha()) {
+                result.setMustntStartWithNonAlpha(ruleConf.isMustntStartWithNonAlpha());
             }
-            if (!fpps.isMustEndWithNonAlpha()) {
-                fpps.setMustEndWithNonAlpha(ruleConf.isMustEndWithNonAlpha());
+            if (!result.isMustEndWithNonAlpha()) {
+                result.setMustEndWithNonAlpha(ruleConf.isMustEndWithNonAlpha());
             }
-            if (!fpps.isMustntEndWithNonAlpha()) {
-                fpps.setMustntEndWithNonAlpha(ruleConf.isMustntEndWithNonAlpha());
+            if (!result.isMustntEndWithNonAlpha()) {
+                result.setMustntEndWithNonAlpha(ruleConf.isMustntEndWithNonAlpha());
             }
-            if (!fpps.isMustEndWithAlpha()) {
-                fpps.setMustEndWithAlpha(ruleConf.isMustEndWithAlpha());
+            if (!result.isMustEndWithAlpha()) {
+                result.setMustEndWithAlpha(ruleConf.isMustEndWithAlpha());
             }
-            if (!fpps.isMustntEndWithAlpha()) {
-                fpps.setMustntEndWithAlpha(ruleConf.isMustntEndWithAlpha());
+            if (!result.isMustntEndWithAlpha()) {
+                result.setMustntEndWithAlpha(ruleConf.isMustntEndWithAlpha());
+            }
+            if (!result.isUsernameAllowed()) {
+                result.setUsernameAllowed(ruleConf.isUsernameAllowed());
             }
         }
 
-        if (fpps.getMinLength() == 0) {
-            fpps.setMinLength(fpps.getMaxLength() < MIN_LENGTH_IF_ZERO ? fpps.getMaxLength() : MIN_LENGTH_IF_ZERO);
+        if (result.getMinLength() == 0) {
+            result.setMinLength(
+                    result.getMaxLength() < MIN_LENGTH_IF_ZERO ? result.getMaxLength() : MIN_LENGTH_IF_ZERO);
         }
 
-        return fpps;
+        return result;
     }
 
     private void check(final DefaultPasswordRuleConf defaultPasswordRuleConf)

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/misc/src/main/java/org/apache/syncope/core/misc/security/PasswordGenerator.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/security/PasswordGenerator.java b/core/misc/src/main/java/org/apache/syncope/core/misc/security/PasswordGenerator.java
index d5e2e14..936dae5 100644
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/security/PasswordGenerator.java
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/security/PasswordGenerator.java
@@ -18,315 +18,15 @@
  */
 package org.apache.syncope.core.misc.security;
 
-import java.util.ArrayList;
 import java.util.List;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.lib.types.PasswordPolicySpec;
-import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.common.lib.policy.PasswordRuleConf;
+import org.apache.syncope.core.misc.policy.InvalidPasswordRuleConf;
 import org.apache.syncope.core.persistence.api.entity.user.User;
-import org.apache.syncope.core.misc.policy.InvalidPasswordPolicySpecException;
-import org.apache.syncope.core.misc.policy.PolicyPattern;
-import org.apache.syncope.core.persistence.api.dao.RealmDAO;
-import org.apache.syncope.core.persistence.api.dao.UserDAO;
-import org.apache.syncope.core.persistence.api.entity.Realm;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-/**
- * Generate random passwords according to given policies.
- * When no minimum and / or maximum length are specified, default values are set.
- *
- * @see org.apache.syncope.core.persistence.api.entity.PasswordPolicy
- */
-@Component
-public class PasswordGenerator {
-
-    private static final char[] SPECIAL_CHARS = { '!', '£', '%', '&', '(', ')', '?', '#', '$' };
-
-    private static final int VERY_MIN_LENGTH = 0;
-
-    private static final int VERY_MAX_LENGTH = 64;
-
-    private static final int MIN_LENGTH_IF_ZERO = 6;
-
-    @Autowired
-    private UserDAO userDAO;
-
-    @Autowired
-    private RealmDAO realmDAO;
-
-    public String generate(final List<PasswordPolicySpec> ppSpecs) throws InvalidPasswordPolicySpecException {
-        PasswordPolicySpec policySpec = merge(ppSpecs);
-
-        check(policySpec);
-
-        return generate(policySpec);
-    }
-
-    public String generate(final User user)
-            throws InvalidPasswordPolicySpecException {
-
-        List<PasswordPolicySpec> ppSpecs = new ArrayList<>();
-
-        for (Realm ancestor : realmDAO.findAncestors(user.getRealm())) {
-            if (ancestor.getPasswordPolicy() != null
-                    && ancestor.getPasswordPolicy().getSpecification() != null) {
-
-                ppSpecs.add(ancestor.getPasswordPolicy().getSpecification());
-            }
-        }
-
-        for (ExternalResource resource : userDAO.findAllResources(user)) {
-            if (resource.getPasswordPolicy() != null
-                    && resource.getPasswordPolicy().getSpecification() != null) {
-
-                ppSpecs.add(resource.getPasswordPolicy().getSpecification());
-            }
-        }
-
-        PasswordPolicySpec policySpec = merge(ppSpecs);
-        check(policySpec);
-        return generate(policySpec);
-    }
-
-    private PasswordPolicySpec merge(final List<PasswordPolicySpec> ppSpecs) {
-        PasswordPolicySpec fpps = new PasswordPolicySpec();
-        fpps.setMinLength(VERY_MIN_LENGTH);
-        fpps.setMaxLength(VERY_MAX_LENGTH);
-
-        for (PasswordPolicySpec policySpec : ppSpecs) {
-            if (policySpec.getMinLength() > fpps.getMinLength()) {
-                fpps.setMinLength(policySpec.getMinLength());
-            }
-
-            if ((policySpec.getMaxLength() != 0) && ((policySpec.getMaxLength() < fpps.getMaxLength()))) {
-                fpps.setMaxLength(policySpec.getMaxLength());
-            }
-            fpps.getPrefixesNotPermitted().addAll(policySpec.getPrefixesNotPermitted());
-            fpps.getSuffixesNotPermitted().addAll(policySpec.getSuffixesNotPermitted());
-
-            if (!fpps.isNonAlphanumericRequired()) {
-                fpps.setNonAlphanumericRequired(policySpec.isNonAlphanumericRequired());
-            }
-
-            if (!fpps.isAlphanumericRequired()) {
-                fpps.setAlphanumericRequired(policySpec.isAlphanumericRequired());
-            }
-            if (!fpps.isDigitRequired()) {
-                fpps.setDigitRequired(policySpec.isDigitRequired());
-            }
-
-            if (!fpps.isLowercaseRequired()) {
-                fpps.setLowercaseRequired(policySpec.isLowercaseRequired());
-            }
-            if (!fpps.isUppercaseRequired()) {
-                fpps.setUppercaseRequired(policySpec.isUppercaseRequired());
-            }
-            if (!fpps.isMustStartWithDigit()) {
-                fpps.setMustStartWithDigit(policySpec.isMustStartWithDigit());
-            }
-            if (!fpps.isMustntStartWithDigit()) {
-                fpps.setMustntStartWithDigit(policySpec.isMustntStartWithDigit());
-            }
-            if (!fpps.isMustEndWithDigit()) {
-                fpps.setMustEndWithDigit(policySpec.isMustEndWithDigit());
-            }
-            if (fpps.isMustntEndWithDigit()) {
-                fpps.setMustntEndWithDigit(policySpec.isMustntEndWithDigit());
-            }
-            if (!fpps.isMustStartWithAlpha()) {
-                fpps.setMustStartWithAlpha(policySpec.isMustStartWithAlpha());
-            }
-            if (!fpps.isMustntStartWithAlpha()) {
-                fpps.setMustntStartWithAlpha(policySpec.isMustntStartWithAlpha());
-            }
-            if (!fpps.isMustStartWithNonAlpha()) {
-                fpps.setMustStartWithNonAlpha(policySpec.isMustStartWithNonAlpha());
-            }
-            if (!fpps.isMustntStartWithNonAlpha()) {
-                fpps.setMustntStartWithNonAlpha(policySpec.isMustntStartWithNonAlpha());
-            }
-            if (!fpps.isMustEndWithNonAlpha()) {
-                fpps.setMustEndWithNonAlpha(policySpec.isMustEndWithNonAlpha());
-            }
-            if (!fpps.isMustntEndWithNonAlpha()) {
-                fpps.setMustntEndWithNonAlpha(policySpec.isMustntEndWithNonAlpha());
-            }
-            if (!fpps.isMustEndWithAlpha()) {
-                fpps.setMustEndWithAlpha(policySpec.isMustEndWithAlpha());
-            }
-            if (!fpps.isMustntEndWithAlpha()) {
-                fpps.setMustntEndWithAlpha(policySpec.isMustntEndWithAlpha());
-            }
-        }
-
-        if (fpps.getMinLength() == 0) {
-            fpps.setMinLength(fpps.getMaxLength() < MIN_LENGTH_IF_ZERO ? fpps.getMaxLength() : MIN_LENGTH_IF_ZERO);
-        }
-
-        return fpps;
-    }
-
-    private void check(final PasswordPolicySpec policySpec)
-            throws InvalidPasswordPolicySpecException {
-
-        if (policySpec.isMustEndWithAlpha() && policySpec.isMustntEndWithAlpha()) {
-            throw new InvalidPasswordPolicySpecException(
-                    "mustEndWithAlpha and mustntEndWithAlpha are both true");
-        }
-        if (policySpec.isMustEndWithAlpha() && policySpec.isMustEndWithDigit()) {
-            throw new InvalidPasswordPolicySpecException(
-                    "mustEndWithAlpha and mustEndWithDigit are both true");
-        }
-        if (policySpec.isMustEndWithDigit() && policySpec.isMustntEndWithDigit()) {
-            throw new InvalidPasswordPolicySpecException(
-                    "mustEndWithDigit and mustntEndWithDigit are both true");
-        }
-        if (policySpec.isMustEndWithNonAlpha() && policySpec.isMustntEndWithNonAlpha()) {
-            throw new InvalidPasswordPolicySpecException(
-                    "mustEndWithNonAlpha and mustntEndWithNonAlpha are both true");
-        }
-        if (policySpec.isMustStartWithAlpha() && policySpec.isMustntStartWithAlpha()) {
-            throw new InvalidPasswordPolicySpecException(
-                    "mustStartWithAlpha and mustntStartWithAlpha are both true");
-        }
-        if (policySpec.isMustStartWithAlpha() && policySpec.isMustStartWithDigit()) {
-            throw new InvalidPasswordPolicySpecException(
-                    "mustStartWithAlpha and mustStartWithDigit are both true");
-        }
-        if (policySpec.isMustStartWithDigit() && policySpec.isMustntStartWithDigit()) {
-            throw new InvalidPasswordPolicySpecException(
-                    "mustStartWithDigit and mustntStartWithDigit are both true");
-        }
-        if (policySpec.isMustStartWithNonAlpha() && policySpec.isMustntStartWithNonAlpha()) {
-            throw new InvalidPasswordPolicySpecException(
-                    "mustStartWithNonAlpha and mustntStartWithNonAlpha are both true");
-        }
-        if (policySpec.getMinLength() > policySpec.getMaxLength()) {
-            throw new InvalidPasswordPolicySpecException("Minimun length (" + policySpec.getMinLength() + ")"
-                    + "is greater than maximum length (" + policySpec.getMaxLength() + ")");
-        }
-    }
-
-    private String generate(final PasswordPolicySpec policySpec) {
-        String[] generatedPassword = new String[policySpec.getMinLength()];
-
-        for (int i = 0; i < generatedPassword.length; i++) {
-            generatedPassword[i] = StringUtils.EMPTY;
-        }
-
-        checkStartChar(generatedPassword, policySpec);
-
-        checkEndChar(generatedPassword, policySpec);
-
-        checkRequired(generatedPassword, policySpec);
-
-        for (int firstEmptyChar = firstEmptyChar(generatedPassword);
-                firstEmptyChar < generatedPassword.length - 1; firstEmptyChar++) {
-
-            generatedPassword[firstEmptyChar] = SecureRandomUtils.generateRandomLetter();
-        }
-
-        checkPrefixAndSuffix(generatedPassword, policySpec);
-
-        return StringUtils.join(generatedPassword);
-    }
-
-    private void checkStartChar(final String[] generatedPassword, final PasswordPolicySpec policySpec) {
-        if (policySpec.isMustStartWithAlpha()) {
-            generatedPassword[0] = SecureRandomUtils.generateRandomLetter();
-        }
-        if (policySpec.isMustStartWithNonAlpha() || policySpec.isMustStartWithDigit()) {
-            generatedPassword[0] = SecureRandomUtils.generateRandomNumber();
-        }
-        if (policySpec.isMustntStartWithAlpha()) {
-            generatedPassword[0] = SecureRandomUtils.generateRandomNumber();
-        }
-        if (policySpec.isMustntStartWithDigit()) {
-            generatedPassword[0] = SecureRandomUtils.generateRandomLetter();
-        }
-        if (policySpec.isMustntStartWithNonAlpha()) {
-            generatedPassword[0] = SecureRandomUtils.generateRandomLetter();
-        }
-
-        if (StringUtils.EMPTY.equals(generatedPassword[0])) {
-            generatedPassword[0] = SecureRandomUtils.generateRandomLetter();
-        }
-    }
-
-    private void checkEndChar(final String[] generatedPassword, final PasswordPolicySpec policySpec) {
-        if (policySpec.isMustEndWithAlpha()) {
-            generatedPassword[policySpec.getMinLength() - 1] = SecureRandomUtils.generateRandomLetter();
-        }
-        if (policySpec.isMustEndWithNonAlpha() || policySpec.isMustEndWithDigit()) {
-            generatedPassword[policySpec.getMinLength() - 1] = SecureRandomUtils.generateRandomNumber();
-        }
-
-        if (policySpec.isMustntEndWithAlpha()) {
-            generatedPassword[policySpec.getMinLength() - 1] = SecureRandomUtils.generateRandomNumber();
-        }
-        if (policySpec.isMustntEndWithDigit()) {
-            generatedPassword[policySpec.getMinLength() - 1] = SecureRandomUtils.generateRandomLetter();
-        }
-        if (policySpec.isMustntEndWithNonAlpha()) {
-            generatedPassword[policySpec.getMinLength() - 1] = SecureRandomUtils.generateRandomLetter();
-        }
-
-        if (StringUtils.EMPTY.equals(generatedPassword[policySpec.getMinLength() - 1])) {
-            generatedPassword[policySpec.getMinLength() - 1] = SecureRandomUtils.generateRandomLetter();
-        }
-    }
-
-    private int firstEmptyChar(final String[] generatedPStrings) {
-        int index = 0;
-        while (!generatedPStrings[index].isEmpty()) {
-            index++;
-        }
-        return index;
-    }
-
-    private void checkRequired(final String[] generatedPassword, final PasswordPolicySpec policySpec) {
-        if (policySpec.isDigitRequired()
-                && !PolicyPattern.DIGIT.matcher(StringUtils.join(generatedPassword)).matches()) {
-
-            generatedPassword[firstEmptyChar(generatedPassword)] = SecureRandomUtils.generateRandomNumber();
-        }
-
-        if (policySpec.isUppercaseRequired()
-                && !PolicyPattern.ALPHA_UPPERCASE.matcher(StringUtils.join(generatedPassword)).matches()) {
-
-            generatedPassword[firstEmptyChar(generatedPassword)] =
-                    SecureRandomUtils.generateRandomLetter().toUpperCase();
-        }
-
-        if (policySpec.isLowercaseRequired()
-                && !PolicyPattern.ALPHA_LOWERCASE.matcher(StringUtils.join(generatedPassword)).matches()) {
-
-            generatedPassword[firstEmptyChar(generatedPassword)] =
-                    SecureRandomUtils.generateRandomLetter().toLowerCase();
-        }
-
-        if (policySpec.isNonAlphanumericRequired()
-                && !PolicyPattern.NON_ALPHANUMERIC.matcher(StringUtils.join(generatedPassword)).matches()) {
 
-            generatedPassword[firstEmptyChar(generatedPassword)] =
-                    SecureRandomUtils.generateRandomSpecialCharacter(SPECIAL_CHARS);
-        }
-    }
+public interface PasswordGenerator {
 
-    private void checkPrefixAndSuffix(final String[] generatedPassword, final PasswordPolicySpec policySpec) {
-        for (String prefix : policySpec.getPrefixesNotPermitted()) {
-            if (StringUtils.join(generatedPassword).startsWith(prefix)) {
-                checkStartChar(generatedPassword, policySpec);
-            }
-        }
+    String generate(User user) throws InvalidPasswordRuleConf;
 
-        for (String suffix : policySpec.getSuffixesNotPermitted()) {
-            if (StringUtils.join(generatedPassword).endsWith(suffix)) {
-                checkEndChar(generatedPassword, policySpec);
-            }
-        }
-    }
+    String generate(List<PasswordRuleConf> ruleConfs) throws InvalidPasswordRuleConf;
 
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/misc/src/main/resources/security.properties
----------------------------------------------------------------------
diff --git a/core/misc/src/main/resources/security.properties b/core/misc/src/main/resources/security.properties
index a0557b5..73db510 100644
--- a/core/misc/src/main/resources/security.properties
+++ b/core/misc/src/main/resources/security.properties
@@ -29,3 +29,4 @@ digester.invertPositionOfPlainSaltInEncryptionResults=true
 digester.invertPositionOfSaltInMessageBeforeDigesting=true
 digester.useLenientSaltSizeCheck=true
 
+passwordGenerator=org.apache.syncope.core.misc.security.DefaultPasswordGenerator

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/misc/src/main/resources/securityContext.xml
----------------------------------------------------------------------
diff --git a/core/misc/src/main/resources/securityContext.xml b/core/misc/src/main/resources/securityContext.xml
index 1022815..711e2f3 100644
--- a/core/misc/src/main/resources/securityContext.xml
+++ b/core/misc/src/main/resources/securityContext.xml
@@ -32,6 +32,7 @@ under the License.
     <constructor-arg value="${anonymousUser}"/>
   </bean>
   
+  <bean class="${passwordGenerator}"/>
   <bean class="org.apache.syncope.core.misc.spring.DefaultRolesPrefixPostProcessor"/>
   
   <security:global-method-security pre-post-annotations="enabled"/>

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/misc/src/test/java/org/apache/syncope/core/misc/security/PasswordGeneratorTest.java
----------------------------------------------------------------------
diff --git a/core/misc/src/test/java/org/apache/syncope/core/misc/security/PasswordGeneratorTest.java b/core/misc/src/test/java/org/apache/syncope/core/misc/security/PasswordGeneratorTest.java
index e3e26e0..536fa60 100644
--- a/core/misc/src/test/java/org/apache/syncope/core/misc/security/PasswordGeneratorTest.java
+++ b/core/misc/src/test/java/org/apache/syncope/core/misc/security/PasswordGeneratorTest.java
@@ -25,114 +25,118 @@ import static org.junit.Assert.fail;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import org.apache.syncope.common.lib.types.PasswordPolicySpec;
-import org.apache.syncope.core.misc.policy.InvalidPasswordPolicySpecException;
+import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf;
+import org.apache.syncope.common.lib.policy.PasswordRuleConf;
+import org.apache.syncope.core.misc.policy.InvalidPasswordRuleConf;
 import org.apache.syncope.core.misc.policy.PolicyPattern;
 import org.junit.Test;
 
 public class PasswordGeneratorTest {
 
-    private final PasswordGenerator passwordGenerator = new PasswordGenerator();
-
-    private PasswordPolicySpec createBasePasswordPolicySpec() {
-        PasswordPolicySpec basePasswordPolicySpec = new PasswordPolicySpec();
-        basePasswordPolicySpec.setAlphanumericRequired(false);
-        basePasswordPolicySpec.setDigitRequired(false);
-        basePasswordPolicySpec.setLowercaseRequired(false);
-        basePasswordPolicySpec.setMaxLength(1000);
-        basePasswordPolicySpec.setMinLength(8);
-        basePasswordPolicySpec.setMustEndWithAlpha(false);
-        basePasswordPolicySpec.setMustEndWithDigit(false);
-        basePasswordPolicySpec.setMustEndWithNonAlpha(false);
-        basePasswordPolicySpec.setMustStartWithAlpha(false);
-        basePasswordPolicySpec.setMustStartWithDigit(false);
-        basePasswordPolicySpec.setMustStartWithNonAlpha(false);
-        basePasswordPolicySpec.setMustntEndWithAlpha(false);
-        basePasswordPolicySpec.setMustntEndWithDigit(false);
-        basePasswordPolicySpec.setMustntEndWithNonAlpha(false);
-        basePasswordPolicySpec.setMustntStartWithAlpha(false);
-        basePasswordPolicySpec.setMustntStartWithDigit(false);
-        basePasswordPolicySpec.setMustntStartWithNonAlpha(false);
-        basePasswordPolicySpec.setNonAlphanumericRequired(false);
-        basePasswordPolicySpec.setUppercaseRequired(false);
-        return basePasswordPolicySpec;
+    private final DefaultPasswordGenerator passwordGenerator = new DefaultPasswordGenerator();
+
+    private DefaultPasswordRuleConf createBaseDefaultPasswordRuleConf() {
+        DefaultPasswordRuleConf baseDefaultPasswordRuleConf = new DefaultPasswordRuleConf();
+        baseDefaultPasswordRuleConf.setAlphanumericRequired(false);
+        baseDefaultPasswordRuleConf.setDigitRequired(false);
+        baseDefaultPasswordRuleConf.setLowercaseRequired(false);
+        baseDefaultPasswordRuleConf.setMaxLength(1000);
+        baseDefaultPasswordRuleConf.setMinLength(8);
+        baseDefaultPasswordRuleConf.setMustEndWithAlpha(false);
+        baseDefaultPasswordRuleConf.setMustEndWithDigit(false);
+        baseDefaultPasswordRuleConf.setMustEndWithNonAlpha(false);
+        baseDefaultPasswordRuleConf.setMustStartWithAlpha(false);
+        baseDefaultPasswordRuleConf.setMustStartWithDigit(false);
+        baseDefaultPasswordRuleConf.setMustStartWithNonAlpha(false);
+        baseDefaultPasswordRuleConf.setMustntEndWithAlpha(false);
+        baseDefaultPasswordRuleConf.setMustntEndWithDigit(false);
+        baseDefaultPasswordRuleConf.setMustntEndWithNonAlpha(false);
+        baseDefaultPasswordRuleConf.setMustntStartWithAlpha(false);
+        baseDefaultPasswordRuleConf.setMustntStartWithDigit(false);
+        baseDefaultPasswordRuleConf.setMustntStartWithNonAlpha(false);
+        baseDefaultPasswordRuleConf.setNonAlphanumericRequired(false);
+        baseDefaultPasswordRuleConf.setUppercaseRequired(false);
+        return baseDefaultPasswordRuleConf;
     }
 
     @Test
-    public void startEndWithDigit() throws InvalidPasswordPolicySpecException {
-        PasswordPolicySpec passwordPolicySpec = createBasePasswordPolicySpec();
-        passwordPolicySpec.setMustStartWithDigit(true);
-
-        PasswordPolicySpec passwordPolicySpec2 = createBasePasswordPolicySpec();
-        passwordPolicySpec.setMustEndWithDigit(true);
-        List<PasswordPolicySpec> passwordPolicySpecs = new ArrayList<>();
-        passwordPolicySpecs.add(passwordPolicySpec);
-        passwordPolicySpecs.add(passwordPolicySpec2);
-        String generatedPassword = passwordGenerator.generate(passwordPolicySpecs);
+    public void startEndWithDigit() throws InvalidPasswordRuleConf {
+        DefaultPasswordRuleConf pwdRuleConf = createBaseDefaultPasswordRuleConf();
+        pwdRuleConf.setMustStartWithDigit(true);
+
+        DefaultPasswordRuleConf pwdRuleConf2 = createBaseDefaultPasswordRuleConf();
+        pwdRuleConf2.setMustEndWithDigit(true);
+
+        List<PasswordRuleConf> ruleConfs = new ArrayList<>();
+        ruleConfs.add(pwdRuleConf);
+        ruleConfs.add(pwdRuleConf2);
+        String generatedPassword = passwordGenerator.generate(ruleConfs);
         assertTrue(Character.isDigit(generatedPassword.charAt(0)));
         assertTrue(Character.isDigit(generatedPassword.charAt(generatedPassword.length() - 1)));
     }
 
     @Test
-    public void startWithDigitAndWithAlpha() throws InvalidPasswordPolicySpecException {
-        PasswordPolicySpec passwordPolicySpec = createBasePasswordPolicySpec();
-        passwordPolicySpec.setMustStartWithDigit(true);
-
-        PasswordPolicySpec passwordPolicySpec2 = createBasePasswordPolicySpec();
-        passwordPolicySpec.setMustEndWithAlpha(true);
-        List<PasswordPolicySpec> passwordPolicySpecs = new ArrayList<>();
-        passwordPolicySpecs.add(passwordPolicySpec);
-        passwordPolicySpecs.add(passwordPolicySpec2);
-        String generatedPassword = passwordGenerator.generate(passwordPolicySpecs);
+    public void startWithDigitAndWithAlpha() throws InvalidPasswordRuleConf {
+        DefaultPasswordRuleConf pwdRuleConf = createBaseDefaultPasswordRuleConf();
+        pwdRuleConf.setMustStartWithDigit(true);
+
+        DefaultPasswordRuleConf pwdRuleConf2 = createBaseDefaultPasswordRuleConf();
+        pwdRuleConf2.setMustEndWithAlpha(true);
+
+        List<PasswordRuleConf> pwdRuleConfs = new ArrayList<>();
+        pwdRuleConfs.add(pwdRuleConf);
+        pwdRuleConfs.add(pwdRuleConf2);
+        String generatedPassword = passwordGenerator.generate(pwdRuleConfs);
         assertTrue(Character.isDigit(generatedPassword.charAt(0)));
         assertTrue(Character.isLetter(generatedPassword.charAt(generatedPassword.length() - 1)));
     }
 
     @Test
-    public void passwordWithNonAlpha() throws InvalidPasswordPolicySpecException {
-        PasswordPolicySpec passwordPolicySpec = createBasePasswordPolicySpec();
-        passwordPolicySpec.setNonAlphanumericRequired(true);
-
-        PasswordPolicySpec passwordPolicySpec2 = createBasePasswordPolicySpec();
-        passwordPolicySpec.setMustEndWithAlpha(true);
-        List<PasswordPolicySpec> passwordPolicySpecs = new ArrayList<>();
-        passwordPolicySpecs.add(passwordPolicySpec);
-        passwordPolicySpecs.add(passwordPolicySpec2);
-        String generatedPassword = passwordGenerator.generate(passwordPolicySpecs);
+    public void passwordWithNonAlpha() throws InvalidPasswordRuleConf {
+        DefaultPasswordRuleConf pwdRuleConf = createBaseDefaultPasswordRuleConf();
+        pwdRuleConf.setNonAlphanumericRequired(true);
+
+        DefaultPasswordRuleConf pwdRuleConf2 = createBaseDefaultPasswordRuleConf();
+        pwdRuleConf2.setMustEndWithAlpha(true);
+
+        List<PasswordRuleConf> pwdRuleConfs = new ArrayList<>();
+        pwdRuleConfs.add(pwdRuleConf);
+        pwdRuleConfs.add(pwdRuleConf2);
+        String generatedPassword = passwordGenerator.generate(pwdRuleConfs);
         assertTrue(PolicyPattern.NON_ALPHANUMERIC.matcher(generatedPassword).matches());
         assertTrue(Character.isLetter(generatedPassword.charAt(generatedPassword.length() - 1)));
     }
 
-    @Test(expected = InvalidPasswordPolicySpecException.class)
-    public void incopatiblePolicies() throws InvalidPasswordPolicySpecException {
-        PasswordPolicySpec passwordPolicySpec = createBasePasswordPolicySpec();
-        passwordPolicySpec.setMinLength(12);
+    @Test(expected = InvalidPasswordRuleConf.class)
+    public void incopatiblePolicies() throws InvalidPasswordRuleConf {
+        DefaultPasswordRuleConf pwdRuleConf = createBaseDefaultPasswordRuleConf();
+        pwdRuleConf.setMinLength(12);
 
-        PasswordPolicySpec passwordPolicySpec2 = createBasePasswordPolicySpec();
-        passwordPolicySpec.setMaxLength(10);
+        DefaultPasswordRuleConf pwdRuleConf2 = createBaseDefaultPasswordRuleConf();
+        pwdRuleConf.setMaxLength(10);
 
-        List<PasswordPolicySpec> passwordPolicySpecs = new ArrayList<>();
-        passwordPolicySpecs.add(passwordPolicySpec);
-        passwordPolicySpecs.add(passwordPolicySpec2);
-        passwordGenerator.generate(passwordPolicySpecs);
+        List<PasswordRuleConf> pwdRuleConfs = new ArrayList<>();
+        pwdRuleConfs.add(pwdRuleConf);
+        pwdRuleConfs.add(pwdRuleConf2);
+        passwordGenerator.generate(pwdRuleConfs);
     }
 
     @Test
     public void issueSYNCOPE678() {
         String password = null;
         try {
-            password = passwordGenerator.generate(Collections.<PasswordPolicySpec>emptyList());
-        } catch (InvalidPasswordPolicySpecException e) {
+            password = passwordGenerator.generate(Collections.<PasswordRuleConf>emptyList());
+        } catch (InvalidPasswordRuleConf e) {
             fail(e.getMessage());
         }
         assertNotNull(password);
 
-        PasswordPolicySpec ppSpec = createBasePasswordPolicySpec();
+        DefaultPasswordRuleConf ppSpec = createBaseDefaultPasswordRuleConf();
         ppSpec.setMinLength(0);
         password = null;
         try {
-            password = passwordGenerator.generate(Collections.singletonList(ppSpec));
-        } catch (InvalidPasswordPolicySpecException e) {
+            password = passwordGenerator.generate(Collections.<PasswordRuleConf>singletonList(ppSpec));
+        } catch (InvalidPasswordRuleConf e) {
             fail(e.getMessage());
         }
         assertNotNull(password);

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AccountRule.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AccountRule.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AccountRule.java
index 25e9e86..25145de 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AccountRule.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AccountRule.java
@@ -19,9 +19,12 @@
 package org.apache.syncope.core.persistence.api.dao;
 
 import org.apache.syncope.common.lib.policy.AccountRuleConf;
+import org.apache.syncope.core.persistence.api.entity.user.User;
 
-public interface AccountRule extends PolicyRule {
-
-    void setConf(AccountRuleConf conf);
+/**
+ * Interface for enforcing a given account rule to user.
+ */
+public interface AccountRule {
 
+    void enforce(AccountRuleConf conf, User user);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PasswordRule.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PasswordRule.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PasswordRule.java
index 4f92092..4cb1ba9 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PasswordRule.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PasswordRule.java
@@ -19,9 +19,12 @@
 package org.apache.syncope.core.persistence.api.dao;
 
 import org.apache.syncope.common.lib.policy.PasswordRuleConf;
+import org.apache.syncope.core.persistence.api.entity.user.User;
 
-public interface PasswordRule extends PolicyRule {
-
-    void setConf(PasswordRuleConf conf);
+/**
+ * Interface for enforcing a given password rule to user.
+ */
+public interface PasswordRule {
 
+    void enforce(PasswordRuleConf conf, User user);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PolicyDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PolicyDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PolicyDAO.java
index 58281ff..a27f1c0 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PolicyDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/PolicyDAO.java
@@ -20,7 +20,7 @@ package org.apache.syncope.core.persistence.api.dao;
 
 import java.util.List;
 import org.apache.syncope.common.lib.types.PolicyType;
-import org.apache.syncope.core.persistence.api.entity.AccountPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.Policy;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AccountPolicy.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AccountPolicy.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AccountPolicy.java
deleted file mode 100644
index 72f8cac..0000000
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AccountPolicy.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.persistence.api.entity;
-
-import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
-import java.util.Set;
-import org.apache.syncope.common.lib.types.AccountPolicySpec;
-
-public interface AccountPolicy extends Policy {
-
-    AccountPolicySpec getSpecification();
-
-    void setSpecification(AccountPolicySpec spec);
-    
-    boolean add(ExternalResource resource);
-
-    boolean remove(ExternalResource resource);
-
-    Set<String> getResourceNames();
-
-    Set<? extends ExternalResource> getResources();
-}

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

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

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Realm.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Realm.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Realm.java
index ab8fcac..ae3290f 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Realm.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Realm.java
@@ -18,6 +18,9 @@
  */
 package org.apache.syncope.core.persistence.api.entity;
 
+import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
+
 public interface Realm extends Entity<Long> {
 
     String getName();

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Report.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Report.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Report.java
index 618e612..30245d0 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Report.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Report.java
@@ -35,7 +35,7 @@ public interface Report extends Entity<Long> {
 
     boolean add(ReportletConf reportletConf);
 
-    boolean remove(ReportletConf reportletConf);
+    void removeAllReportletConfs();
 
     List<? extends ReportletConf> getReportletConfs();
 

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


[08/33] syncope git commit: Restoring Travis CI notifications via e-mail

Posted by md...@apache.org.
Restoring Travis CI notifications via e-mail


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

Branch: refs/heads/SYNCOPE-156
Commit: bc41d9bc36075e5f7e3c80d9be0cdfbd782b0b05
Parents: dce78db
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Thu Aug 20 11:38:40 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Thu Aug 20 11:38:40 2015 +0200

----------------------------------------------------------------------
 .travis.yml | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/bc41d9bc/.travis.yml
----------------------------------------------------------------------
diff --git a/.travis.yml b/.travis.yml
index 9ecafc7..68bdf96 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -21,4 +21,5 @@ install: mvn --show-version --errors -P skipTests
 #invoker.streamLogs: we cannot access to log files through Travis web ui, so display everything in the console
 script: mvn --show-version --errors clean install -Dinvoker.streamLogs=true
 notifications:
-  webhooks: http://rovere.tirasa.net/cgi-bin/travis.cgi
+  email:
+    - dev@syncope.apache.org


[03/33] syncope git commit: [SYNCOPE-685] Pre: more cleanup

Posted by md...@apache.org.
[SYNCOPE-685] Pre: more cleanup


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

Branch: refs/heads/SYNCOPE-156
Commit: 2cf761973049f874380d5ac054b4daf95360fc57
Parents: fc91e51
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Mon Aug 17 17:56:15 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Mon Aug 17 17:56:15 2015 +0200

----------------------------------------------------------------------
 .../common/lib/types/PushPolicySpec.java        | 28 ++++++++++
 .../syncope/core/misc/ConnObjectUtils.java      |  8 +--
 .../core/misc/policy/PolicyEvaluator.java       |  6 +-
 .../core/misc/security/PasswordGenerator.java   |  8 +--
 .../persistence/api/entity/AccountPolicy.java   |  5 ++
 .../persistence/api/entity/PasswordPolicy.java  |  8 ++-
 .../core/persistence/api/entity/Policy.java     |  5 --
 .../core/persistence/api/entity/PushPolicy.java |  7 ++-
 .../core/persistence/api/entity/SyncPolicy.java |  8 ++-
 .../jpa/entity/JPAAccountPolicy.java            | 12 ++++
 .../jpa/entity/JPAPasswordPolicy.java           | 12 ++++
 .../core/persistence/jpa/entity/JPAPolicy.java  | 16 +-----
 .../persistence/jpa/entity/JPAPushPolicy.java   | 16 ++++--
 .../persistence/jpa/entity/JPASyncPolicy.java   | 12 ++++
 .../jpa/validation/entity/PolicyCheck.java      | 41 --------------
 .../jpa/validation/entity/PolicyValidator.java  | 59 --------------------
 .../core/persistence/jpa/inner/PolicyTest.java  | 31 +++-------
 .../java/data/PolicyDataBinderImpl.java         | 18 +++---
 .../provisioning/java/sync/SyncJobDelegate.java |  2 +-
 .../core/provisioning/java/sync/SyncUtils.java  |  2 +-
 20 files changed, 132 insertions(+), 172 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/2cf76197/common/lib/src/main/java/org/apache/syncope/common/lib/types/PushPolicySpec.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/PushPolicySpec.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/PushPolicySpec.java
new file mode 100644
index 0000000..d7a5640
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/PushPolicySpec.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.common.lib.types;
+
+import javax.xml.bind.annotation.XmlType;
+
+@XmlType
+public class PushPolicySpec implements PolicySpec {
+
+    private static final long serialVersionUID = 3641030189482617497L;
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2cf76197/core/misc/src/main/java/org/apache/syncope/core/misc/ConnObjectUtils.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/ConnObjectUtils.java b/core/misc/src/main/java/org/apache/syncope/core/misc/ConnObjectUtils.java
index 6257d59..de1bcfb 100644
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/ConnObjectUtils.java
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/ConnObjectUtils.java
@@ -123,9 +123,9 @@ public class ConnObjectUtils {
             if (realm != null) {
                 for (Realm ancestor : realmDAO.findAncestors(realm)) {
                     if (ancestor.getPasswordPolicy() != null
-                            && ancestor.getPasswordPolicy().getSpecification(PasswordPolicySpec.class) != null) {
+                            && ancestor.getPasswordPolicy().getSpecification() != null) {
 
-                        ppSpecs.add(ancestor.getPasswordPolicy().getSpecification(PasswordPolicySpec.class));
+                        ppSpecs.add(ancestor.getPasswordPolicy().getSpecification());
                     }
                 }
             }
@@ -133,9 +133,9 @@ public class ConnObjectUtils {
             for (String resName : userTO.getResources()) {
                 ExternalResource resource = resourceDAO.find(resName);
                 if (resource != null && resource.getPasswordPolicy() != null
-                        && resource.getPasswordPolicy().getSpecification(PasswordPolicySpec.class) != null) {
+                        && resource.getPasswordPolicy().getSpecification() != null) {
 
-                    ppSpecs.add(resource.getPasswordPolicy().getSpecification(PasswordPolicySpec.class));
+                    ppSpecs.add(resource.getPasswordPolicy().getSpecification());
                 }
             }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/2cf76197/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PolicyEvaluator.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PolicyEvaluator.java b/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PolicyEvaluator.java
index 425568d..f7d946c 100644
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PolicyEvaluator.java
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PolicyEvaluator.java
@@ -22,7 +22,9 @@ import java.util.List;
 import org.apache.syncope.common.lib.types.AccountPolicySpec;
 import org.apache.syncope.common.lib.types.PasswordPolicySpec;
 import org.apache.syncope.common.lib.types.PolicySpec;
+import org.apache.syncope.core.persistence.api.entity.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.Any;
+import org.apache.syncope.core.persistence.api.entity.PasswordPolicy;
 import org.apache.syncope.core.persistence.api.entity.PlainAttr;
 import org.apache.syncope.core.persistence.api.entity.Policy;
 import org.apache.syncope.core.persistence.api.entity.user.User;
@@ -41,7 +43,7 @@ public class PolicyEvaluator {
         T result;
         switch (policy.getType()) {
             case PASSWORD:
-                PasswordPolicySpec ppSpec = policy.getSpecification(PasswordPolicySpec.class);
+                PasswordPolicySpec ppSpec = ((PasswordPolicy) policy).getSpecification();
                 PasswordPolicySpec evaluatedPPSpec = new PasswordPolicySpec();
 
                 BeanUtils.copyProperties(ppSpec, evaluatedPPSpec, new String[] { "schemasNotPermitted" });
@@ -71,7 +73,7 @@ public class PolicyEvaluator {
                 break;
 
             case ACCOUNT:
-                AccountPolicySpec spec = policy.getSpecification(AccountPolicySpec.class);
+                AccountPolicySpec spec = ((AccountPolicy) policy).getSpecification();
                 AccountPolicySpec accountPolicy = new AccountPolicySpec();
 
                 BeanUtils.copyProperties(spec, accountPolicy, new String[] { "schemasNotPermitted" });

http://git-wip-us.apache.org/repos/asf/syncope/blob/2cf76197/core/misc/src/main/java/org/apache/syncope/core/misc/security/PasswordGenerator.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/security/PasswordGenerator.java b/core/misc/src/main/java/org/apache/syncope/core/misc/security/PasswordGenerator.java
index 9514f27..d5e2e14 100644
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/security/PasswordGenerator.java
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/security/PasswordGenerator.java
@@ -71,17 +71,17 @@ public class PasswordGenerator {
 
         for (Realm ancestor : realmDAO.findAncestors(user.getRealm())) {
             if (ancestor.getPasswordPolicy() != null
-                    && ancestor.getPasswordPolicy().getSpecification(PasswordPolicySpec.class) != null) {
+                    && ancestor.getPasswordPolicy().getSpecification() != null) {
 
-                ppSpecs.add(ancestor.getPasswordPolicy().getSpecification(PasswordPolicySpec.class));
+                ppSpecs.add(ancestor.getPasswordPolicy().getSpecification());
             }
         }
 
         for (ExternalResource resource : userDAO.findAllResources(user)) {
             if (resource.getPasswordPolicy() != null
-                    && resource.getPasswordPolicy().getSpecification(PasswordPolicySpec.class) != null) {
+                    && resource.getPasswordPolicy().getSpecification() != null) {
 
-                ppSpecs.add(resource.getPasswordPolicy().getSpecification(PasswordPolicySpec.class));
+                ppSpecs.add(resource.getPasswordPolicy().getSpecification());
             }
         }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/2cf76197/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AccountPolicy.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AccountPolicy.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AccountPolicy.java
index bafc554..72f8cac 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AccountPolicy.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AccountPolicy.java
@@ -20,9 +20,14 @@ package org.apache.syncope.core.persistence.api.entity;
 
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import java.util.Set;
+import org.apache.syncope.common.lib.types.AccountPolicySpec;
 
 public interface AccountPolicy extends Policy {
 
+    AccountPolicySpec getSpecification();
+
+    void setSpecification(AccountPolicySpec spec);
+    
     boolean add(ExternalResource resource);
 
     boolean remove(ExternalResource resource);

http://git-wip-us.apache.org/repos/asf/syncope/blob/2cf76197/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/PasswordPolicy.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/PasswordPolicy.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/PasswordPolicy.java
index 4981ff2..a4004ce 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/PasswordPolicy.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/PasswordPolicy.java
@@ -18,6 +18,12 @@
  */
 package org.apache.syncope.core.persistence.api.entity;
 
+import org.apache.syncope.common.lib.types.PasswordPolicySpec;
+
 public interface PasswordPolicy extends Policy {
-    
+
+    PasswordPolicySpec getSpecification();
+
+    void setSpecification(PasswordPolicySpec spec);
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/2cf76197/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Policy.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Policy.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Policy.java
index 5edca88..d02de0a 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Policy.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/Policy.java
@@ -18,18 +18,13 @@
  */
 package org.apache.syncope.core.persistence.api.entity;
 
-import org.apache.syncope.common.lib.types.PolicySpec;
 import org.apache.syncope.common.lib.types.PolicyType;
 
 public interface Policy extends Entity<Long> {
 
     String getDescription();
 
-    <T extends PolicySpec> T getSpecification(Class<T> reference);
-
     PolicyType getType();
 
     void setDescription(String description);
-
-    void setSpecification(PolicySpec policy);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/2cf76197/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/PushPolicy.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/PushPolicy.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/PushPolicy.java
index 761525d..e1c37df 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/PushPolicy.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/PushPolicy.java
@@ -18,6 +18,11 @@
  */
 package org.apache.syncope.core.persistence.api.entity;
 
+import org.apache.syncope.common.lib.types.PushPolicySpec;
+
 public interface PushPolicy extends Policy {
-    
+
+    PushPolicySpec getSpecification();
+
+    void setSpecification(PushPolicySpec spec);
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/2cf76197/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/SyncPolicy.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/SyncPolicy.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/SyncPolicy.java
index 3acb092..1e389d7 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/SyncPolicy.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/SyncPolicy.java
@@ -18,6 +18,12 @@
  */
 package org.apache.syncope.core.persistence.api.entity;
 
+import org.apache.syncope.common.lib.types.SyncPolicySpec;
+
 public interface SyncPolicy extends Policy {
-    
+
+    SyncPolicySpec getSpecification();
+
+    void setSpecification(SyncPolicySpec spec);
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/2cf76197/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAccountPolicy.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAccountPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAccountPolicy.java
index 64475aa..844b49a 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAccountPolicy.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAccountPolicy.java
@@ -28,7 +28,9 @@ import javax.persistence.JoinTable;
 import javax.persistence.ManyToMany;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Transformer;
+import org.apache.syncope.common.lib.types.AccountPolicySpec;
 import org.apache.syncope.common.lib.types.PolicyType;
+import org.apache.syncope.core.misc.serialization.POJOHelper;
 import org.apache.syncope.core.persistence.api.entity.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.persistence.jpa.entity.resource.JPAExternalResource;
@@ -55,6 +57,16 @@ public class JPAAccountPolicy extends JPAPolicy implements AccountPolicy {
     }
 
     @Override
+    public AccountPolicySpec getSpecification() {
+        return POJOHelper.deserialize(specification, AccountPolicySpec.class);
+    }
+
+    @Override
+    public void setSpecification(final AccountPolicySpec policy) {
+        this.specification = POJOHelper.serialize(policy);
+    }
+
+    @Override
     public boolean add(final ExternalResource resource) {
         checkType(resource, JPAExternalResource.class);
         return resources.add((JPAExternalResource) resource);

http://git-wip-us.apache.org/repos/asf/syncope/blob/2cf76197/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPasswordPolicy.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPasswordPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPasswordPolicy.java
index 51f55a5..8a755f4 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPasswordPolicy.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPasswordPolicy.java
@@ -20,7 +20,9 @@ package org.apache.syncope.core.persistence.jpa.entity;
 
 import javax.persistence.DiscriminatorValue;
 import javax.persistence.Entity;
+import org.apache.syncope.common.lib.types.PasswordPolicySpec;
 import org.apache.syncope.common.lib.types.PolicyType;
+import org.apache.syncope.core.misc.serialization.POJOHelper;
 import org.apache.syncope.core.persistence.api.entity.PasswordPolicy;
 
 @Entity
@@ -33,4 +35,14 @@ public class JPAPasswordPolicy extends JPAPolicy implements PasswordPolicy {
         super();
         this.type = PolicyType.PASSWORD;
     }
+
+    @Override
+    public PasswordPolicySpec getSpecification() {
+        return POJOHelper.deserialize(specification, PasswordPolicySpec.class);
+    }
+
+    @Override
+    public void setSpecification(final PasswordPolicySpec policy) {
+        this.specification = POJOHelper.serialize(policy);
+    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/2cf76197/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPolicy.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPolicy.java
index 3104c23..1dc5de6 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPolicy.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPolicy.java
@@ -28,17 +28,13 @@ import javax.persistence.InheritanceType;
 import javax.persistence.Lob;
 import javax.persistence.Table;
 import javax.validation.constraints.NotNull;
-import org.apache.syncope.common.lib.types.PolicySpec;
 import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.syncope.core.persistence.api.entity.Policy;
-import org.apache.syncope.core.persistence.jpa.validation.entity.PolicyCheck;
-import org.apache.syncope.core.misc.serialization.POJOHelper;
 
 @Entity
 @Table(name = JPAPolicy.TABLE)
 @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
 @DiscriminatorColumn(name = "DTYPE")
-@PolicyCheck
 public abstract class JPAPolicy extends AbstractEntity<Long> implements Policy {
 
     private static final long serialVersionUID = -5844833125843247458L;
@@ -56,7 +52,7 @@ public abstract class JPAPolicy extends AbstractEntity<Long> implements Policy {
     protected PolicyType type;
 
     @Lob
-    private String specification;
+    protected String specification;
 
     @Override
     public Long getKey() {
@@ -77,14 +73,4 @@ public abstract class JPAPolicy extends AbstractEntity<Long> implements Policy {
     public PolicyType getType() {
         return type;
     }
-
-    @Override
-    public <T extends PolicySpec> T getSpecification(final Class<T> reference) {
-        return POJOHelper.deserialize(specification, reference);
-    }
-
-    @Override
-    public void setSpecification(final PolicySpec policy) {
-        this.specification = POJOHelper.serialize(policy);
-    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/2cf76197/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPushPolicy.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPushPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPushPolicy.java
index f4be722..c3440b2 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPushPolicy.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPushPolicy.java
@@ -20,6 +20,8 @@ package org.apache.syncope.core.persistence.jpa.entity;
 
 import javax.persistence.Entity;
 import org.apache.syncope.common.lib.types.PolicyType;
+import org.apache.syncope.common.lib.types.PushPolicySpec;
+import org.apache.syncope.core.misc.serialization.POJOHelper;
 import org.apache.syncope.core.persistence.api.entity.PushPolicy;
 
 @Entity
@@ -28,12 +30,18 @@ public class JPAPushPolicy extends JPAPolicy implements PushPolicy {
     private static final long serialVersionUID = -5875589156893921113L;
 
     public JPAPushPolicy() {
-        this(false);
-    }
-
-    public JPAPushPolicy(final boolean global) {
         super();
         this.type = PolicyType.PUSH;
     }
 
+    @Override
+    public PushPolicySpec getSpecification() {
+        return POJOHelper.deserialize(specification, PushPolicySpec.class);
+    }
+
+    @Override
+    public void setSpecification(final PushPolicySpec policy) {
+        this.specification = POJOHelper.serialize(policy);
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/2cf76197/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASyncPolicy.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASyncPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASyncPolicy.java
index 39b7565..e244223 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASyncPolicy.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASyncPolicy.java
@@ -20,7 +20,9 @@ package org.apache.syncope.core.persistence.jpa.entity;
 
 import javax.persistence.DiscriminatorValue;
 import javax.persistence.Entity;
+import org.apache.syncope.common.lib.types.SyncPolicySpec;
 import org.apache.syncope.common.lib.types.PolicyType;
+import org.apache.syncope.core.misc.serialization.POJOHelper;
 import org.apache.syncope.core.persistence.api.entity.SyncPolicy;
 
 @Entity
@@ -33,4 +35,14 @@ public class JPASyncPolicy extends JPAPolicy implements SyncPolicy {
         super();
         this.type = PolicyType.SYNC;
     }
+
+    @Override
+    public SyncPolicySpec getSpecification() {
+        return POJOHelper.deserialize(specification, SyncPolicySpec.class);
+    }
+
+    @Override
+    public void setSpecification(final SyncPolicySpec policy) {
+        this.specification = POJOHelper.serialize(policy);
+    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/2cf76197/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PolicyCheck.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PolicyCheck.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PolicyCheck.java
deleted file mode 100644
index 45873be..0000000
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PolicyCheck.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.persistence.jpa.validation.entity;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-import javax.validation.Constraint;
-import javax.validation.Payload;
-
-@Target({ ElementType.TYPE })
-@Retention(RetentionPolicy.RUNTIME)
-@Constraint(validatedBy = PolicyValidator.class)
-@Documented
-public @interface PolicyCheck {
-
-    String message() default "{org.apache.syncope.core.persistence.validation.policy}";
-
-    Class<?>[] groups() default {};
-
-    Class<? extends Payload>[] payload() default {};
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2cf76197/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PolicyValidator.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PolicyValidator.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PolicyValidator.java
deleted file mode 100644
index fd95c4c..0000000
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/validation/entity/PolicyValidator.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.persistence.jpa.validation.entity;
-
-import javax.validation.ConstraintValidatorContext;
-import org.apache.syncope.common.lib.types.AccountPolicySpec;
-import org.apache.syncope.common.lib.types.EntityViolationType;
-import org.apache.syncope.common.lib.types.PasswordPolicySpec;
-import org.apache.syncope.common.lib.types.SyncPolicySpec;
-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;
-
-public class PolicyValidator extends AbstractValidator<PolicyCheck, Policy> {
-
-    @Override
-    public boolean isValid(final Policy object, final ConstraintValidatorContext context) {
-        context.disableDefaultConstraintViolation();
-
-        EntityViolationType violationType =
-                object instanceof PasswordPolicy
-                && !(object.getSpecification(PasswordPolicySpec.class) instanceof PasswordPolicySpec)
-                        ? EntityViolationType.InvalidPasswordPolicy
-                        : object instanceof AccountPolicy
-                        && !(object.getSpecification(AccountPolicySpec.class) instanceof AccountPolicySpec)
-                                ? EntityViolationType.InvalidAccountPolicy
-                                : object instanceof SyncPolicy
-                                && !(object.getSpecification(SyncPolicySpec.class) instanceof SyncPolicySpec)
-                                        ? EntityViolationType.InvalidSyncPolicy
-                                        : null;
-
-        if (violationType != null) {
-            context.buildConstraintViolationWithTemplate(getTemplate(violationType,
-                    "Invalid policy specification")).addPropertyNode("specification").
-                    addConstraintViolation();
-
-            return false;
-        }
-
-        return true;
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/2cf76197/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java
index 603cd01..780d192 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java
@@ -31,9 +31,9 @@ import org.apache.syncope.common.lib.types.PasswordPolicySpec;
 import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.syncope.common.lib.types.SyncPolicySpec;
 import org.apache.syncope.core.misc.serialization.POJOHelper;
-import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
 import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
+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.persistence.jpa.AbstractTest;
@@ -62,7 +62,7 @@ public class PolicyTest extends AbstractTest {
         SyncPolicy policy = policyDAO.find(3L);
         assertNotNull("findById did not work", policy);
 
-        SyncPolicySpec spec = policy.getSpecification(SyncPolicySpec.class);
+        SyncPolicySpec spec = policy.getSpecification();
         assertNotNull(spec);
 
         String rule = spec.getCorrelationRules().get(AnyTypeKind.USER.name());
@@ -81,19 +81,6 @@ public class PolicyTest extends AbstractTest {
         assertFalse(policies.isEmpty());
     }
 
-    @Test(expected = InvalidEntityException.class)
-    public void saveInvalidPolicy() {
-        PasswordPolicySpec passwordPolicy = new PasswordPolicySpec();
-        passwordPolicy.setMaxLength(8);
-        passwordPolicy.setMinLength(6);
-
-        SyncPolicy policy = entityFactory.newEntity(SyncPolicy.class);
-        policy.setSpecification(passwordPolicy);
-        policy.setDescription("sync policy");
-
-        policyDAO.save(policy);
-    }
-
     @Test
     public void create() {
         SyncPolicy policy = entityFactory.newEntity(SyncPolicy.class);
@@ -113,10 +100,10 @@ public class PolicyTest extends AbstractTest {
 
         assertNotNull(policy);
         assertEquals(PolicyType.SYNC, policy.getType());
-        assertEquals(syncURuleName, (policy.getSpecification(SyncPolicySpec.class)).
-                getCorrelationRules().get(anyTypeDAO.findUser().getKey()));
-        assertEquals(syncGRuleName, (policy.getSpecification(SyncPolicySpec.class)).
-                getCorrelationRules().get(anyTypeDAO.findGroup().getKey()));
+        assertEquals(syncURuleName,
+                policy.getSpecification().getCorrelationRules().get(anyTypeDAO.findUser().getKey()));
+        assertEquals(syncGRuleName,
+                policy.getSpecification().getCorrelationRules().get(anyTypeDAO.findGroup().getKey()));
     }
 
     @Test
@@ -125,7 +112,7 @@ public class PolicyTest extends AbstractTest {
         specification.setMaxLength(8);
         specification.setMinLength(6);
 
-        Policy policy = policyDAO.find(2L);
+        PasswordPolicy policy = policyDAO.find(2L);
         assertNotNull(policy);
         policy.setSpecification(specification);
 
@@ -133,8 +120,8 @@ public class PolicyTest extends AbstractTest {
 
         assertNotNull(policy);
         assertEquals(PolicyType.PASSWORD, policy.getType());
-        assertEquals((policy.getSpecification(PasswordPolicySpec.class)).getMaxLength(), 8);
-        assertEquals((policy.getSpecification(PasswordPolicySpec.class)).getMinLength(), 6);
+        assertEquals(policy.getSpecification().getMaxLength(), 8);
+        assertEquals(policy.getSpecification().getMinLength(), 6);
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/syncope/blob/2cf76197/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java
index fa53bbd..7180b6c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java
@@ -24,10 +24,7 @@ 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.AccountPolicySpec;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
-import org.apache.syncope.common.lib.types.PasswordPolicySpec;
-import org.apache.syncope.common.lib.types.SyncPolicySpec;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
 import org.apache.syncope.core.persistence.api.entity.AccountPolicy;
@@ -59,24 +56,23 @@ public class PolicyDataBinderImpl implements PolicyDataBinder {
     @SuppressWarnings("unchecked")
     @Override
     public <T extends AbstractPolicyTO> T getPolicyTO(final Policy policy) {
-        final T policyTO;
-
+        T policyTO;
         switch (policy.getType()) {
             case PASSWORD:
                 policyTO = (T) new PasswordPolicyTO();
-                ((PasswordPolicyTO) policyTO).setSpecification(policy.getSpecification(PasswordPolicySpec.class));
+                ((PasswordPolicyTO) policyTO).setSpecification(((PasswordPolicy) policy).getSpecification());
                 break;
 
             case ACCOUNT:
                 policyTO = (T) new AccountPolicyTO();
-                ((AccountPolicyTO) policyTO).setSpecification(policy.getSpecification(AccountPolicySpec.class));
+                ((AccountPolicyTO) policyTO).setSpecification(((AccountPolicy) policy).getSpecification());
                 ((AccountPolicyTO) policyTO).getResources().addAll(((AccountPolicy) policy).getResourceNames());
                 break;
 
             case SYNC:
             default:
                 policyTO = (T) new SyncPolicyTO();
-                ((SyncPolicyTO) policyTO).setSpecification(policy.getSpecification(SyncPolicySpec.class));
+                ((SyncPolicyTO) policyTO).setSpecification(((SyncPolicy) policy).getSpecification());
         }
 
         policyTO.setKey(policy.getKey());
@@ -120,7 +116,7 @@ public class PolicyDataBinderImpl implements PolicyDataBinder {
                 if (result == null) {
                     result = (T) entityFactory.newEntity(PasswordPolicy.class);
                 }
-                result.setSpecification(((PasswordPolicyTO) policyTO).getSpecification());
+                ((PasswordPolicy) result).setSpecification(((PasswordPolicyTO) policyTO).getSpecification());
                 break;
 
             case ACCOUNT:
@@ -131,7 +127,7 @@ public class PolicyDataBinderImpl implements PolicyDataBinder {
                 if (result == null) {
                     result = (T) entityFactory.newEntity(AccountPolicy.class);
                 }
-                result.setSpecification(((AccountPolicyTO) policyTO).getSpecification());
+                ((AccountPolicy) result).setSpecification(((AccountPolicyTO) policyTO).getSpecification());
 
                 if (((AccountPolicy) result).getResources() != null
                         && !((AccountPolicy) result).getResources().isEmpty()) {
@@ -155,7 +151,7 @@ public class PolicyDataBinderImpl implements PolicyDataBinder {
                 if (result == null) {
                     result = (T) entityFactory.newEntity(SyncPolicy.class);
                 }
-                result.setSpecification(((SyncPolicyTO) policyTO).getSpecification());
+                ((SyncPolicy) result).setSpecification(((SyncPolicyTO) policyTO).getSpecification());
         }
 
         result.setDescription(policyTO.getDescription());

http://git-wip-us.apache.org/repos/asf/syncope/blob/2cf76197/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncJobDelegate.java
index 245a74a..2e70e80 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncJobDelegate.java
@@ -210,7 +210,7 @@ public class SyncJobDelegate extends AbstractProvisioningJobDelegate<SyncTask> {
         if (task instanceof SyncTask) {
             syncPolicySpec = task.getResource().getSyncPolicy() == null
                     ? null
-                    : task.getResource().getSyncPolicy().getSpecification(SyncPolicySpec.class);
+                    : task.getResource().getSyncPolicy().getSpecification();
         } else {
             syncPolicySpec = null;
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/2cf76197/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncUtils.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncUtils.java
index 7fd244b..4878362 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncUtils.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncUtils.java
@@ -279,7 +279,7 @@ public class SyncUtils {
 
         SyncPolicySpec syncPolicySpec = null;
         if (provision.getResource().getSyncPolicy() != null) {
-            syncPolicySpec = provision.getResource().getSyncPolicy().getSpecification(SyncPolicySpec.class);
+            syncPolicySpec = provision.getResource().getSyncPolicy().getSpecification();
         }
 
         SyncCorrelationRule syncRule = null;


[22/33] syncope git commit: Cleaning up implementation classes discovery

Posted by md...@apache.org.
Cleaning up implementation classes discovery


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

Branch: refs/heads/SYNCOPE-156
Commit: 5da611de22243abc94021f4560667ad1d80a6991
Parents: 966484b
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Mon Aug 24 13:30:43 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Mon Aug 24 13:30:43 2015 +0200

----------------------------------------------------------------------
 .../apache/syncope/core/logic/SyncopeLogic.java |  27 ++-
 .../init/ClassPathScanImplementationLookup.java | 200 +++++++++++++++++++
 .../init/ImplementationClassNamesLoader.java    | 157 ---------------
 .../core/logic/report/AbstractReportlet.java    |   1 +
 .../core/logic/report/GroupReportlet.java       |   1 +
 .../core/logic/report/ReportJobDelegate.java    |  42 +---
 .../syncope/core/logic/report/Reportlet.java    |  40 ----
 .../core/logic/report/ReportletConfClass.java   |  32 ---
 .../core/logic/report/StaticReportlet.java      |   1 +
 .../core/logic/report/UserReportlet.java        |   1 +
 .../persistence/api/ImplementationLookup.java   |  53 +++++
 .../core/persistence/api/dao/Reportlet.java     |  40 ++++
 .../persistence/api/dao/ReportletConfClass.java |  32 +++
 .../core/persistence/jpa/dao/JPAUserDAO.java    |  77 ++-----
 .../jpa/DummyImplementationLookup.java          |  73 +++++++
 .../java/DummyImplementationLookup.java         |  73 +++++++
 16 files changed, 513 insertions(+), 337 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
index eb0c1ad..1354f67 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
@@ -18,8 +18,6 @@
  */
 package org.apache.syncope.core.logic;
 
-import static org.apache.syncope.core.logic.init.ImplementationClassNamesLoader.Type;
-
 import java.io.IOException;
 import java.lang.reflect.Method;
 import java.net.URI;
@@ -27,9 +25,10 @@ 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.security.PasswordGenerator;
 import org.apache.syncope.core.misc.spring.ResourceWithFallbackLoader;
+import org.apache.syncope.core.persistence.api.ImplementationLookup;
+import org.apache.syncope.core.persistence.api.ImplementationLookup.Type;
 import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager;
 import org.apache.syncope.core.provisioning.api.AnyTransformer;
@@ -87,7 +86,7 @@ public class SyncopeLogic extends AbstractLogic<SyncopeTO> {
     private PasswordGenerator passwordGenerator;
 
     @Autowired
-    private ImplementationClassNamesLoader classNamesLoader;
+    private ImplementationLookup implementationLookup;
 
     @Resource(name = "velocityResourceLoader")
     private ResourceWithFallbackLoader resourceLoader;
@@ -135,16 +134,16 @@ public class SyncopeLogic extends AbstractLogic<SyncopeTO> {
         syncopeTO.setVirAttrCache(virAttrCache.getClass().getName());
         syncopeTO.setPasswordGenerator(passwordGenerator.getClass().getName());
 
-        syncopeTO.getReportlets().addAll(classNamesLoader.getClassNames(Type.REPORTLET));
-        syncopeTO.getAccountRules().addAll(classNamesLoader.getClassNames(Type.ACCOUNT_RULE));
-        syncopeTO.getPasswordRules().addAll(classNamesLoader.getClassNames(Type.PASSWORD_RULE));
-        syncopeTO.getTaskJobs().addAll(classNamesLoader.getClassNames(Type.TASKJOBDELEGATE));
-        syncopeTO.getPropagationActions().addAll(classNamesLoader.getClassNames(Type.PROPAGATION_ACTIONS));
-        syncopeTO.getSyncActions().addAll(classNamesLoader.getClassNames(Type.SYNC_ACTIONS));
-        syncopeTO.getPushActions().addAll(classNamesLoader.getClassNames(Type.PUSH_ACTIONS));
-        syncopeTO.getSyncCorrelationRules().addAll(classNamesLoader.getClassNames(Type.SYNC_CORRELATION_RULE));
-        syncopeTO.getPushCorrelationRules().addAll(classNamesLoader.getClassNames(Type.PUSH_CORRELATION_RULE));
-        syncopeTO.getValidators().addAll(classNamesLoader.getClassNames(Type.VALIDATOR));
+        syncopeTO.getReportlets().addAll(implementationLookup.getClassNames(Type.REPORTLET));
+        syncopeTO.getAccountRules().addAll(implementationLookup.getClassNames(Type.ACCOUNT_RULE));
+        syncopeTO.getPasswordRules().addAll(implementationLookup.getClassNames(Type.PASSWORD_RULE));
+        syncopeTO.getTaskJobs().addAll(implementationLookup.getClassNames(Type.TASKJOBDELEGATE));
+        syncopeTO.getPropagationActions().addAll(implementationLookup.getClassNames(Type.PROPAGATION_ACTIONS));
+        syncopeTO.getSyncActions().addAll(implementationLookup.getClassNames(Type.SYNC_ACTIONS));
+        syncopeTO.getPushActions().addAll(implementationLookup.getClassNames(Type.PUSH_ACTIONS));
+        syncopeTO.getSyncCorrelationRules().addAll(implementationLookup.getClassNames(Type.SYNC_CORRELATION_RULE));
+        syncopeTO.getPushCorrelationRules().addAll(implementationLookup.getClassNames(Type.PUSH_CORRELATION_RULE));
+        syncopeTO.getValidators().addAll(implementationLookup.getClassNames(Type.VALIDATOR));
 
         Set<String> htmlTemplates = new HashSet<>();
         Set<String> textTemplates = new HashSet<>();

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java b/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
new file mode 100644
index 0000000..2305e70
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
@@ -0,0 +1,200 @@
+/*
+ * 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.init;
+
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.policy.AccountRuleConf;
+import org.apache.syncope.common.lib.policy.PasswordRuleConf;
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.core.persistence.api.dao.Reportlet;
+import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
+import org.apache.syncope.core.persistence.api.ImplementationLookup;
+import org.apache.syncope.core.persistence.api.attrvalue.validation.Validator;
+import org.apache.syncope.core.persistence.api.dao.AccountRule;
+import org.apache.syncope.core.persistence.api.dao.AccountRuleConfClass;
+import org.apache.syncope.core.persistence.api.dao.PasswordRule;
+import org.apache.syncope.core.persistence.api.dao.PasswordRuleConfClass;
+import org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate;
+import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
+import org.apache.syncope.core.provisioning.api.sync.PushActions;
+import org.apache.syncope.core.provisioning.api.sync.SyncActions;
+import org.apache.syncope.core.provisioning.api.sync.SyncCorrelationRule;
+import org.apache.syncope.core.provisioning.java.sync.PushJobDelegate;
+import org.apache.syncope.core.provisioning.java.sync.SyncJobDelegate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
+import org.springframework.core.type.filter.AssignableTypeFilter;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ClassUtils;
+
+/**
+ * Cache class names for all implementations of Syncope interfaces found in classpath, for later usage.
+ */
+@Component
+public class ClassPathScanImplementationLookup implements ImplementationLookup {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ImplementationLookup.class);
+
+    private Map<Type, Set<String>> classNames;
+
+    private Map<Class<? extends ReportletConf>, Class<? extends Reportlet>> reportletClasses;
+
+    private Map<Class<? extends AccountRuleConf>, Class<? extends AccountRule>> accountRuleClasses;
+
+    private Map<Class<? extends PasswordRuleConf>, Class<? extends PasswordRule>> passwordRuleClasses;
+
+    @Override
+    public Integer getPriority() {
+        return 400;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public void load() {
+        classNames = new EnumMap<>(Type.class);
+        for (Type type : Type.values()) {
+            classNames.put(type, new HashSet<String>());
+        }
+
+        reportletClasses = new HashMap<>();
+        accountRuleClasses = new HashMap<>();
+        passwordRuleClasses = new HashMap<>();
+
+        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
+        scanner.addIncludeFilter(new AssignableTypeFilter(Reportlet.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(AccountRule.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(PasswordRule.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(SchedTaskJobDelegate.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(SyncActions.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(PushActions.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(SyncCorrelationRule.class));
+        // Remove once SYNCOPE-470 is done
+        //scanner.addIncludeFilter(new AssignableTypeFilter(PushCorrelationRule.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(PropagationActions.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(Validator.class));
+
+        for (BeanDefinition bd : scanner.findCandidateComponents(StringUtils.EMPTY)) {
+            try {
+                Class<?> clazz = ClassUtils.resolveClassName(
+                        bd.getBeanClassName(), ClassUtils.getDefaultClassLoader());
+                boolean isAbsractClazz = Modifier.isAbstract(clazz.getModifiers());
+
+                if (Reportlet.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                    classNames.get(Type.REPORTLET).add(clazz.getName());
+
+                    ReportletConfClass annotation = clazz.getAnnotation(ReportletConfClass.class);
+                    if (annotation != null) {
+                        reportletClasses.put(annotation.value(), (Class<? extends Reportlet>) clazz);
+                    }
+                }
+
+                if (AccountRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                    classNames.get(Type.ACCOUNT_RULE).add(clazz.getName());
+
+                    AccountRuleConfClass annotation = clazz.getAnnotation(AccountRuleConfClass.class);
+                    if (annotation != null) {
+                        accountRuleClasses.put(annotation.value(), (Class<? extends AccountRule>) clazz);
+                    }
+                }
+                if (PasswordRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                    classNames.get(Type.PASSWORD_RULE).add(clazz.getName());
+                    PasswordRuleConfClass annotation = clazz.getAnnotation(PasswordRuleConfClass.class);
+                    if (annotation != null) {
+                        passwordRuleClasses.put(annotation.value(), (Class<? extends PasswordRule>) clazz);
+                    }
+                }
+
+                if (SchedTaskJobDelegate.class.isAssignableFrom(clazz) && !isAbsractClazz
+                        && !SyncJobDelegate.class.isAssignableFrom(clazz)
+                        && !PushJobDelegate.class.isAssignableFrom(clazz)) {
+
+                    classNames.get(Type.TASKJOBDELEGATE).add(bd.getBeanClassName());
+                }
+
+                if (SyncActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                    classNames.get(Type.SYNC_ACTIONS).add(bd.getBeanClassName());
+                }
+
+                if (PushActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                    classNames.get(Type.PUSH_ACTIONS).add(bd.getBeanClassName());
+                }
+
+                if (SyncCorrelationRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                    classNames.get(Type.SYNC_CORRELATION_RULE).add(bd.getBeanClassName());
+                }
+
+                // Uncomment when SYNCOPE-470 is done
+                /* if (PushCorrelationRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                 * classNames.get(Type.PUSH_CORRELATION_RULES).add(metadata.getClassName());
+                 * } */
+                if (PropagationActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                    classNames.get(Type.PROPAGATION_ACTIONS).add(bd.getBeanClassName());
+                }
+
+                if (Validator.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                    classNames.get(Type.VALIDATOR).add(bd.getBeanClassName());
+                }
+            } catch (Throwable t) {
+                LOG.warn("Could not inspect class {}", bd.getBeanClassName(), t);
+            }
+        }
+        classNames = Collections.unmodifiableMap(classNames);
+        reportletClasses = Collections.unmodifiableMap(reportletClasses);
+        accountRuleClasses = Collections.unmodifiableMap(accountRuleClasses);
+        passwordRuleClasses = Collections.unmodifiableMap(passwordRuleClasses);
+
+        LOG.debug("Implementation classes found: {}", classNames);
+    }
+
+    @Override
+    public Set<String> getClassNames(final Type type) {
+        return classNames.get(type);
+    }
+
+    @Override
+    public Class<? extends Reportlet> getReportletClass(
+            final Class<? extends ReportletConf> reportletConfClass) {
+
+        return reportletClasses.get(reportletConfClass);
+    }
+
+    @Override
+    public Class<? extends AccountRule> getAccountRuleClass(
+            final Class<? extends AccountRuleConf> accountRuleConfClass) {
+
+        return accountRuleClasses.get(accountRuleConfClass);
+    }
+
+    @Override
+    public Class<? extends PasswordRule> getPasswordRuleClass(
+            final Class<? extends PasswordRuleConf> passwordRuleConfClass) {
+
+        return passwordRuleClasses.get(passwordRuleConfClass);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/logic/src/main/java/org/apache/syncope/core/logic/init/ImplementationClassNamesLoader.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/init/ImplementationClassNamesLoader.java b/core/logic/src/main/java/org/apache/syncope/core/logic/init/ImplementationClassNamesLoader.java
deleted file mode 100644
index 7bc842e..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/init/ImplementationClassNamesLoader.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.init;
-
-import java.lang.reflect.Modifier;
-import java.util.Collections;
-import java.util.EnumMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
-import org.apache.syncope.core.provisioning.api.sync.PushActions;
-import org.apache.syncope.core.provisioning.api.sync.SyncActions;
-import org.apache.syncope.core.provisioning.api.sync.SyncCorrelationRule;
-import org.apache.syncope.core.logic.report.Reportlet;
-import org.apache.syncope.core.persistence.api.SyncopeLoader;
-import org.apache.syncope.core.persistence.api.attrvalue.validation.Validator;
-import org.apache.syncope.core.persistence.api.dao.AccountRule;
-import org.apache.syncope.core.persistence.api.dao.PasswordRule;
-import org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate;
-import org.apache.syncope.core.provisioning.java.sync.PushJobDelegate;
-import org.apache.syncope.core.provisioning.java.sync.SyncJobDelegate;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.config.BeanDefinition;
-import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
-import org.springframework.core.type.filter.AssignableTypeFilter;
-import org.springframework.stereotype.Component;
-import org.springframework.util.ClassUtils;
-
-/**
- * Cache class names for all implementations of Syncope interfaces found in classpath, for later usage.
- */
-@Component
-public class ImplementationClassNamesLoader implements SyncopeLoader {
-
-    public enum Type {
-
-        REPORTLET,
-        ACCOUNT_RULE,
-        PASSWORD_RULE,
-        TASKJOBDELEGATE,
-        PROPAGATION_ACTIONS,
-        SYNC_ACTIONS,
-        PUSH_ACTIONS,
-        SYNC_CORRELATION_RULE,
-        PUSH_CORRELATION_RULE,
-        VALIDATOR
-
-    }
-
-    private static final Logger LOG = LoggerFactory.getLogger(ImplementationClassNamesLoader.class);
-
-    private Map<Type, Set<String>> classNames;
-
-    @Override
-    public Integer getPriority() {
-        return 400;
-    }
-
-    @Override
-    public void load() {
-        classNames = new EnumMap<>(Type.class);
-        for (Type type : Type.values()) {
-            classNames.put(type, new HashSet<String>());
-        }
-
-        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
-        scanner.addIncludeFilter(new AssignableTypeFilter(Reportlet.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(AccountRule.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(PasswordRule.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(SchedTaskJobDelegate.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(SyncActions.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(PushActions.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(SyncCorrelationRule.class));
-        // Remove once SYNCOPE-470 is done
-        //scanner.addIncludeFilter(new AssignableTypeFilter(PushCorrelationRule.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(PropagationActions.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(Validator.class));
-
-        for (BeanDefinition bd : scanner.findCandidateComponents(StringUtils.EMPTY)) {
-            try {
-                Class<?> clazz = ClassUtils.resolveClassName(
-                        bd.getBeanClassName(), ClassUtils.getDefaultClassLoader());
-                boolean isAbsractClazz = Modifier.isAbstract(clazz.getModifiers());
-
-                if (Reportlet.class.isAssignableFrom(clazz) && !isAbsractClazz) {
-                    classNames.get(Type.REPORTLET).add(clazz.getName());
-                }
-
-                if (AccountRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
-                    classNames.get(Type.ACCOUNT_RULE).add(clazz.getName());
-                }
-                if (PasswordRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
-                    classNames.get(Type.PASSWORD_RULE).add(clazz.getName());
-                }
-
-                if (SchedTaskJobDelegate.class.isAssignableFrom(clazz) && !isAbsractClazz
-                        && !SyncJobDelegate.class.isAssignableFrom(clazz)
-                        && !PushJobDelegate.class.isAssignableFrom(clazz)) {
-
-                    classNames.get(Type.TASKJOBDELEGATE).add(bd.getBeanClassName());
-                }
-
-                if (SyncActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
-                    classNames.get(Type.SYNC_ACTIONS).add(bd.getBeanClassName());
-                }
-
-                if (PushActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
-                    classNames.get(Type.PUSH_ACTIONS).add(bd.getBeanClassName());
-                }
-
-                if (SyncCorrelationRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
-                    classNames.get(Type.SYNC_CORRELATION_RULE).add(bd.getBeanClassName());
-                }
-
-                // Uncomment when SYNCOPE-470 is done
-                /* if (PushCorrelationRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
-                 * classNames.get(Type.PUSH_CORRELATION_RULES).add(metadata.getClassName());
-                 * } */
-                if (PropagationActions.class.isAssignableFrom(clazz) && !isAbsractClazz) {
-                    classNames.get(Type.PROPAGATION_ACTIONS).add(bd.getBeanClassName());
-                }
-
-                if (Validator.class.isAssignableFrom(clazz) && !isAbsractClazz) {
-                    classNames.get(Type.VALIDATOR).add(bd.getBeanClassName());
-                }
-            } catch (Throwable t) {
-                LOG.warn("Could not inspect class {}", bd.getBeanClassName(), t);
-            }
-        }
-        classNames = Collections.unmodifiableMap(classNames);
-
-        LOG.debug("Implementation classes found: {}", classNames);
-    }
-
-    public Set<String> getClassNames(final Type type) {
-        return classNames.get(type);
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java
index 482ad0e..69e6b4f 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.logic.report;
 
+import org.apache.syncope.core.persistence.api.dao.Reportlet;
 import org.apache.syncope.common.lib.report.ReportletConf;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
index c5e6642..ec9a745 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.logic.report;
 
+import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java
index d790568..2b63d1b 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportJobDelegate.java
@@ -20,10 +20,7 @@ package org.apache.syncope.core.logic.report;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.lang.reflect.Modifier;
 import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
 import java.util.zip.Deflater;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
@@ -33,14 +30,15 @@ import javax.xml.transform.sax.SAXTransformerFactory;
 import javax.xml.transform.sax.TransformerHandler;
 import javax.xml.transform.stream.StreamResult;
 import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.common.lib.types.ReportExecStatus;
 import org.apache.syncope.core.misc.ExceptionUtils2;
 import org.apache.syncope.core.misc.spring.ApplicationContextProvider;
+import org.apache.syncope.core.persistence.api.ImplementationLookup;
 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.Reportlet;
 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;
@@ -48,13 +46,9 @@ import org.quartz.JobExecutionException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.beans.factory.support.AbstractBeanDefinition;
-import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
-import org.springframework.core.type.filter.AssignableTypeFilter;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
-import org.springframework.util.ClassUtils;
 import org.xml.sax.helpers.AttributesImpl;
 
 @Component
@@ -62,32 +56,6 @@ public class ReportJobDelegate {
 
     private static final Logger LOG = LoggerFactory.getLogger(ReportJobDelegate.class);
 
-    private static final Map<Class<? extends ReportletConf>, Class<Reportlet>> REPORTLET_CLASSES = new HashMap<>();
-
-    static {
-        initReportletClasses();
-    }
-
-    @SuppressWarnings("unchecked")
-    private static void initReportletClasses() {
-        ClassPathScanningCandidateComponentProvider scanner =
-                new ClassPathScanningCandidateComponentProvider(false);
-        scanner.addIncludeFilter(new AssignableTypeFilter(Reportlet.class));
-
-        for (BeanDefinition bd : scanner.findCandidateComponents(StringUtils.EMPTY)) {
-            Class<?> clazz = ClassUtils.resolveClassName(
-                    bd.getBeanClassName(), ClassUtils.getDefaultClassLoader());
-            boolean isAbstract = Modifier.isAbstract(clazz.getModifiers());
-
-            if (Reportlet.class.isAssignableFrom(clazz) && !isAbstract) {
-                ReportletConfClass annotation = clazz.getAnnotation(ReportletConfClass.class);
-                if (annotation != null) {
-                    REPORTLET_CLASSES.put(annotation.value(), (Class<Reportlet>) clazz);
-                }
-            }
-        }
-    }
-
     /**
      * Report DAO.
      */
@@ -103,6 +71,9 @@ public class ReportJobDelegate {
     @Autowired
     private EntityFactory entityFactory;
 
+    @Autowired
+    private ImplementationLookup implementationLookup;
+
     @Transactional
     public void execute(final Long reportKey) throws JobExecutionException {
         Report report = reportDAO.find(reportKey);
@@ -156,7 +127,8 @@ public class ReportJobDelegate {
 
             // iterate over reportlet instances defined for this report
             for (ReportletConf reportletConf : report.getReportletConfs()) {
-                Class<Reportlet> reportletClass = REPORTLET_CLASSES.get(reportletConf.getClass());
+                Class<? extends Reportlet> reportletClass =
+                        implementationLookup.getReportletClass(reportletConf.getClass());
                 if (reportletClass == null) {
                     LOG.warn("Could not find matching reportlet for {}", reportletConf.getClass());
                 } else {

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/logic/src/main/java/org/apache/syncope/core/logic/report/Reportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/Reportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/Reportlet.java
deleted file mode 100644
index 66d00c4..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/Reportlet.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import org.apache.syncope.common.lib.report.ReportletConf;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-
-/**
- * Interface for all elements that can be embedded in a report.
- *
- * @see org.apache.syncope.core.persistence.api.entity.Report
- */
-public interface Reportlet {
-
-    /**
-     * Actual data extraction for reporting.
-     *
-     * @param conf configuration
-     * @param handler SAX content handler for streaming result
-     * @throws SAXException if there is any problem in SAX handling
-     */
-    void extract(ReportletConf conf, ContentHandler handler) throws SAXException;
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportletConfClass.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportletConfClass.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportletConfClass.java
deleted file mode 100644
index 39f1c16..0000000
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/ReportletConfClass.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.logic.report;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import org.apache.syncope.common.lib.report.ReportletConf;
-
-@Target({ ElementType.TYPE })
-@Retention(RetentionPolicy.RUNTIME)
-public @interface ReportletConfClass {
-
-    Class<? extends ReportletConf> value();
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java
index 963d484..b7db39f 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/StaticReportlet.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.logic.report;
 
+import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
 import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.common.lib.report.StaticReportletConf;
 import org.apache.syncope.core.misc.DataFormat;

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
index 44ae0e0..91715a7 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/UserReportlet.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.core.logic.report;
 
+import org.apache.syncope.core.persistence.api.dao.ReportletConfClass;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java
new file mode 100644
index 0000000..b00d3d4
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api;
+
+import java.util.Set;
+import org.apache.syncope.common.lib.policy.AccountRuleConf;
+import org.apache.syncope.common.lib.policy.PasswordRuleConf;
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.core.persistence.api.dao.AccountRule;
+import org.apache.syncope.core.persistence.api.dao.PasswordRule;
+import org.apache.syncope.core.persistence.api.dao.Reportlet;
+
+public interface ImplementationLookup extends SyncopeLoader {
+
+    public enum Type {
+
+        REPORTLET,
+        ACCOUNT_RULE,
+        PASSWORD_RULE,
+        TASKJOBDELEGATE,
+        PROPAGATION_ACTIONS,
+        SYNC_ACTIONS,
+        PUSH_ACTIONS,
+        SYNC_CORRELATION_RULE,
+        PUSH_CORRELATION_RULE,
+        VALIDATOR
+
+    }
+
+    Set<String> getClassNames(Type type);
+
+    Class<? extends Reportlet> getReportletClass(Class<? extends ReportletConf> reportletConfClass);
+
+    Class<? extends AccountRule> getAccountRuleClass(Class<? extends AccountRuleConf> accountRuleConfClass);
+
+    Class<? extends PasswordRule> getPasswordRuleClass(Class<? extends PasswordRuleConf> passwordRuleConfClass);
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/Reportlet.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/Reportlet.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/Reportlet.java
new file mode 100644
index 0000000..8180831
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/Reportlet.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.dao;
+
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * Interface for all elements that can be embedded in a report.
+ *
+ * @see org.apache.syncope.core.persistence.api.entity.Report
+ */
+public interface Reportlet {
+
+    /**
+     * Actual data extraction for reporting.
+     *
+     * @param conf configuration
+     * @param handler SAX content handler for streaming result
+     * @throws SAXException if there is any problem in SAX handling
+     */
+    void extract(ReportletConf conf, ContentHandler handler) throws SAXException;
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ReportletConfClass.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ReportletConfClass.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ReportletConfClass.java
new file mode 100644
index 0000000..c1b3040
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/ReportletConfClass.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.persistence.api.dao;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.apache.syncope.common.lib.report.ReportletConf;
+
+@Target({ ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ReportletConfClass {
+
+    Class<? extends ReportletConf> value();
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
index 130c01e..bdc552e 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
@@ -18,13 +18,10 @@
  */
 package org.apache.syncope.core.persistence.jpa.dao;
 
-import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 import javax.annotation.Resource;
 import javax.persistence.NoResultException;
@@ -32,93 +29,50 @@ import javax.persistence.TypedQuery;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Predicate;
 import org.apache.commons.collections4.Transformer;
-import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.ImmutablePair;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.syncope.common.lib.policy.AccountRuleConf;
+import org.apache.syncope.common.lib.policy.PasswordRuleConf;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.Entitlement;
 import org.apache.syncope.common.lib.types.EntityViolationType;
-import org.apache.syncope.common.lib.policy.PasswordRuleConf;
 import org.apache.syncope.core.misc.policy.AccountPolicyException;
 import org.apache.syncope.core.misc.policy.PasswordPolicyException;
-import org.apache.syncope.core.persistence.api.dao.NotFoundException;
-import org.apache.syncope.core.persistence.api.dao.GroupDAO;
-import org.apache.syncope.core.persistence.api.dao.UserDAO;
-import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
-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.persistence.jpa.entity.user.JPAUser;
 import org.apache.syncope.core.misc.security.AuthContextUtils;
 import org.apache.syncope.core.misc.security.UnauthorizedException;
 import org.apache.syncope.core.misc.spring.ApplicationContextProvider;
+import org.apache.syncope.core.persistence.api.ImplementationLookup;
 import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
 import org.apache.syncope.core.persistence.api.dao.AccountRule;
-import org.apache.syncope.core.persistence.api.dao.AccountRuleConfClass;
+import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.PasswordRule;
-import org.apache.syncope.core.persistence.api.dao.PasswordRuleConfClass;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
 import org.apache.syncope.core.persistence.api.dao.RoleDAO;
-import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
-import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.Role;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
+import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
+import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion;
 import org.apache.syncope.core.persistence.api.entity.user.UMembership;
+import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.persistence.jpa.entity.JPAAnyUtilsFactory;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPADynRoleMembership;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUDynGroupMembership;
+import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.beans.factory.support.AbstractBeanDefinition;
-import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
-import org.springframework.core.type.filter.AssignableTypeFilter;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
-import org.springframework.util.ClassUtils;
 
 @Repository
 public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
 
-    private static final Map<Class<? extends AccountRuleConf>, Class<AccountRule>> ACCOUNT_RULES_CLASSES =
-            new HashMap<>();
-
-    private static final Map<Class<? extends PasswordRuleConf>, Class<PasswordRule>> PASSWORD_RULES_CLASSES =
-            new HashMap<>();
-
-    static {
-        initRules();
-    }
-
-    @SuppressWarnings("unchecked")
-    private static void initRules() {
-        ClassPathScanningCandidateComponentProvider scanner =
-                new ClassPathScanningCandidateComponentProvider(false);
-        scanner.addIncludeFilter(new AssignableTypeFilter(AccountRule.class));
-        scanner.addIncludeFilter(new AssignableTypeFilter(PasswordRule.class));
-
-        for (BeanDefinition bd : scanner.findCandidateComponents(StringUtils.EMPTY)) {
-            Class<?> clazz = ClassUtils.resolveClassName(
-                    bd.getBeanClassName(), ClassUtils.getDefaultClassLoader());
-            boolean isAbstract = Modifier.isAbstract(clazz.getModifiers());
-
-            if (AccountRule.class.isAssignableFrom(clazz) && !isAbstract) {
-                AccountRuleConfClass annotation = clazz.getAnnotation(AccountRuleConfClass.class);
-                if (annotation != null) {
-                    ACCOUNT_RULES_CLASSES.put(annotation.value(), (Class<AccountRule>) clazz);
-                }
-            }
-            if (PasswordRule.class.isAssignableFrom(clazz) && !isAbstract) {
-                PasswordRuleConfClass annotation = clazz.getAnnotation(PasswordRuleConfClass.class);
-                if (annotation != null) {
-                    PASSWORD_RULES_CLASSES.put(annotation.value(), (Class<PasswordRule>) clazz);
-                }
-            }
-        }
-    }
-
     @Autowired
     private RealmDAO realmDAO;
 
@@ -128,6 +82,9 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
     @Autowired
     private RoleDAO roleDAO;
 
+    @Autowired
+    private ImplementationLookup implementationLookup;
+
     @Resource(name = "adminUser")
     private String adminUser;
 
@@ -280,7 +237,8 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
                 }
 
                 for (PasswordRuleConf ruleConf : policy.getRuleConfs()) {
-                    Class<PasswordRule> ruleClass = PASSWORD_RULES_CLASSES.get(ruleConf.getClass());
+                    Class<? extends PasswordRule> ruleClass =
+                            implementationLookup.getPasswordRuleClass(ruleConf.getClass());
                     if (ruleClass == null) {
                         LOG.warn("Could not find matching password rule for {}", ruleConf.getClass());
                     } else {
@@ -342,7 +300,8 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
 
             for (AccountPolicy policy : getAccountPolicies(user)) {
                 for (AccountRuleConf ruleConf : policy.getRuleConfs()) {
-                    Class<AccountRule> ruleClass = ACCOUNT_RULES_CLASSES.get(ruleConf.getClass());
+                    Class<? extends AccountRule> ruleClass =
+                            implementationLookup.getAccountRuleClass(ruleConf.getClass());
                     if (ruleClass == null) {
                         LOG.warn("Could not find matching password rule for {}", ruleConf.getClass());
                     } else {

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyImplementationLookup.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyImplementationLookup.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyImplementationLookup.java
new file mode 100644
index 0000000..dd761b4
--- /dev/null
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyImplementationLookup.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.core.persistence.jpa;
+
+import java.util.Collections;
+import java.util.Set;
+import org.apache.syncope.common.lib.policy.AccountRuleConf;
+import org.apache.syncope.common.lib.policy.PasswordRuleConf;
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.core.persistence.api.ImplementationLookup;
+import org.apache.syncope.core.persistence.api.dao.AccountRule;
+import org.apache.syncope.core.persistence.api.dao.PasswordRule;
+import org.apache.syncope.core.persistence.api.dao.Reportlet;
+import org.apache.syncope.core.persistence.jpa.dao.DefaultAccountRule;
+import org.apache.syncope.core.persistence.jpa.dao.DefaultPasswordRule;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DummyImplementationLookup implements ImplementationLookup {
+
+    @Override
+    public Integer getPriority() {
+        return -1;
+    }
+
+    @Override
+    public void load() {
+        // do nothing
+    }
+
+    @Override
+    public Set<String> getClassNames(final Type type) {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public Class<Reportlet> getReportletClass(
+            final Class<? extends ReportletConf> reportletConfClass) {
+
+        return null;
+    }
+
+    @Override
+    public Class<? extends AccountRule> getAccountRuleClass(
+            final Class<? extends AccountRuleConf> accountRuleConfClass) {
+
+        return DefaultAccountRule.class;
+    }
+
+    @Override
+    public Class<? extends PasswordRule> getPasswordRuleClass(
+            final Class<? extends PasswordRuleConf> passwordRuleConfClass) {
+
+        return DefaultPasswordRule.class;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/5da611de/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DummyImplementationLookup.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DummyImplementationLookup.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DummyImplementationLookup.java
new file mode 100644
index 0000000..06715d8
--- /dev/null
+++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DummyImplementationLookup.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.core.provisioning.java;
+
+import java.util.Collections;
+import java.util.Set;
+import org.apache.syncope.common.lib.policy.AccountRuleConf;
+import org.apache.syncope.common.lib.policy.PasswordRuleConf;
+import org.apache.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.core.persistence.api.ImplementationLookup;
+import org.apache.syncope.core.persistence.api.dao.AccountRule;
+import org.apache.syncope.core.persistence.api.dao.PasswordRule;
+import org.apache.syncope.core.persistence.api.dao.Reportlet;
+import org.apache.syncope.core.persistence.jpa.dao.DefaultAccountRule;
+import org.apache.syncope.core.persistence.jpa.dao.DefaultPasswordRule;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DummyImplementationLookup implements ImplementationLookup {
+
+    @Override
+    public Integer getPriority() {
+        return -1;
+    }
+
+    @Override
+    public void load() {
+        // do nothing
+    }
+
+    @Override
+    public Set<String> getClassNames(final Type type) {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public Class<Reportlet> getReportletClass(
+            final Class<? extends ReportletConf> reportletConfClass) {
+
+        return null;
+    }
+
+    @Override
+    public Class<? extends AccountRule> getAccountRuleClass(
+            final Class<? extends AccountRuleConf> accountRuleConfClass) {
+
+        return DefaultAccountRule.class;
+    }
+
+    @Override
+    public Class<? extends PasswordRule> getPasswordRuleClass(
+            final Class<? extends PasswordRuleConf> passwordRuleConfClass) {
+
+        return DefaultPasswordRule.class;
+    }
+
+}


[13/33] syncope git commit: [SYNCOPE-685] Now account and password policies can be composed of several 'rules', where each rule can be provided as a separate Java class (similar to Reportlet mechanism); previous account and password policies are now impl

Posted by md...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java
index 780d192..fc8305b 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/PolicyTest.java
@@ -27,15 +27,15 @@ import static org.junit.Assert.assertTrue;
 import java.util.List;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
-import org.apache.syncope.common.lib.types.PasswordPolicySpec;
+import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf;
 import org.apache.syncope.common.lib.types.PolicyType;
-import org.apache.syncope.common.lib.types.SyncPolicySpec;
+import org.apache.syncope.common.lib.policy.SyncPolicySpec;
 import org.apache.syncope.core.misc.serialization.POJOHelper;
 import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
 import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
-import org.apache.syncope.core.persistence.api.entity.PasswordPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.persistence.api.entity.Policy;
-import org.apache.syncope.core.persistence.api.entity.SyncPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.SyncPolicy;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
 import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -108,20 +108,21 @@ public class PolicyTest extends AbstractTest {
 
     @Test
     public void update() {
-        PasswordPolicySpec specification = new PasswordPolicySpec();
-        specification.setMaxLength(8);
-        specification.setMinLength(6);
+        DefaultPasswordRuleConf ruleConf = new DefaultPasswordRuleConf();
+        ruleConf.setMaxLength(8);
+        ruleConf.setMinLength(6);
 
         PasswordPolicy policy = policyDAO.find(2L);
         assertNotNull(policy);
-        policy.setSpecification(specification);
+        assertEquals(1, policy.getRuleConfs().size());
+        policy.add(ruleConf);
 
         policy = policyDAO.save(policy);
 
         assertNotNull(policy);
         assertEquals(PolicyType.PASSWORD, policy.getType());
-        assertEquals(policy.getSpecification().getMaxLength(), 8);
-        assertEquals(policy.getSpecification().getMinLength(), 6);
+        assertEquals(((DefaultPasswordRuleConf) policy.getRuleConfs().get(1)).getMaxLength(), 8);
+        assertEquals(((DefaultPasswordRuleConf) policy.getRuleConfs().get(1)).getMinLength(), 6);
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RealmTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RealmTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RealmTest.java
index 4eb611a..0fa7c35 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RealmTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/RealmTest.java
@@ -33,8 +33,8 @@ import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntit
 import org.apache.syncope.core.persistence.api.dao.MalformedPathException;
 import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
-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.AccountPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
 import org.junit.Test;

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java
index 7c62ab2..e1d12b1 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java
@@ -32,7 +32,7 @@ import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.entity.user.UPlainAttrValue;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.persistence.jpa.AbstractTest;
-import org.apache.syncope.core.misc.policy.InvalidPasswordPolicySpecException;
+import org.apache.syncope.core.misc.policy.InvalidPasswordRuleConf;
 import org.apache.syncope.core.misc.security.PasswordGenerator;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
 import org.junit.Test;
@@ -214,7 +214,7 @@ public class UserTest extends AbstractTest {
         String password = "";
         try {
             password = passwordGenerator.generate(user);
-        } catch (InvalidPasswordPolicySpecException ex) {
+        } catch (InvalidPasswordRuleConf ex) {
             fail(ex.getMessage());
         }
         assertNotNull(password);
@@ -233,7 +233,7 @@ public class UserTest extends AbstractTest {
         try {
             password = passwordGenerator.generate(user);
 
-        } catch (InvalidPasswordPolicySpecException ex) {
+        } catch (InvalidPasswordRuleConf ex) {
             fail(ex.getMessage());
         }
         assertNotNull(password);

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceTest.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceTest.java
index 967b719..1cd5e47 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/ResourceTest.java
@@ -39,7 +39,7 @@ import org.apache.syncope.core.persistence.api.dao.TaskDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
 import org.apache.syncope.core.persistence.api.entity.ConnInstance;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
-import org.apache.syncope.core.persistence.api.entity.PasswordPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.persistence.api.entity.resource.Mapping;
 import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
 import org.apache.syncope.core.persistence.api.entity.resource.Provision;

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index 5977c9a..6f912b6 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -36,7 +36,7 @@ under the License.
   <PlainSchema name="notificationjob.cronExpression" type="String"
                mandatoryCondition="false" multivalue="0" uniqueConstraint="0" readonly="0"/>
   <CPlainAttr id="2" owner_id="1" schema_name="notificationjob.cronExpression"/>
-  <CPlainAttrValue id="2" attribute_id="2" stringValue="0/5 * * * * ?"/>
+  <CPlainAttrValue id="2" attribute_id="2" stringValue="0/20 * * * * ?"/>
   
   <PlainSchema name="notification.maxRetries" type="Long"
                mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
@@ -100,24 +100,34 @@ under the License.
                mandatoryCondition="false" multivalue="1" uniqueConstraint="0" readonly="0"/>
   
   <!-- sample policies -->
-  <Policy DTYPE="SyncPolicy" id="1" description="a sync policy" type="SYNC" 
-          specification='{"conflictResolutionAction":"IGNORE"'/>
-  <Policy DTYPE="PasswordPolicy" id="2" description="a password policy" type="PASSWORD" 
-          specification='{"historyLength":1,"maxLength":0,"minLength":8,"nonAlphanumericRequired":false,"alphanumericRequired":false,"digitRequired":false,"lowercaseRequired":false,"uppercaseRequired":false,"mustStartWithDigit":false,"mustntStartWithDigit":false,"mustEndWithDigit":false,"mustntEndWithDigit":false,"mustStartWithNonAlpha":false,"mustStartWithAlpha":false,"mustntStartWithNonAlpha":false,"mustntStartWithAlpha":false,"mustEndWithNonAlpha":false,"mustEndWithAlpha":false,"mustntEndWithNonAlpha":false,"mustntEndWithAlpha":false,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":["notpermitted1","notpermitted2"],"suffixesNotPermitted":[],"allowNullPassword":true}'/>
-  <Policy DTYPE="SyncPolicy" id="3" description="sync policy 2" type="SYNC" 
-          specification='{"conflictResolutionAction":"ALL","correlationRules":{"USER":"[\"username\",\"firstname\"]"}}'/>
-  <Policy DTYPE="PasswordPolicy" id="4" description="sample password policy" type="PASSWORD" 
-          specification='{"historyLength":0,"maxLength":0,"minLength":10,"nonAlphanumericRequired":false,"alphanumericRequired":false,"digitRequired":true,"lowercaseRequired":false,"uppercaseRequired":false,"mustStartWithDigit":false,"mustntStartWithDigit":false,"mustEndWithDigit":false,"mustntEndWithDigit":false,"mustStartWithNonAlpha":false,"mustStartWithAlpha":false,"mustntStartWithNonAlpha":false,"mustntStartWithAlpha":false,"mustEndWithNonAlpha":false,"mustEndWithAlpha":false,"mustntEndWithNonAlpha":false,"mustntEndWithAlpha":false,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":["notpermitted1","notpermitted2"],"suffixesNotPermitted":[], "allowNullPassword":true}'/>
-  <Policy DTYPE="AccountPolicy" id="5" description="an account policy" type="ACCOUNT" 
-          specification='{"maxLength":0,"minLength":0,"pattern":null,"allUpperCase":false,"allLowerCase":false,"propagateSuspension":false,"maxAuthenticationAttempts":0,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":[],"suffixesNotPermitted":[]}'/>
-  <Policy DTYPE="AccountPolicy" id="6" description="sample account policy" type="ACCOUNT" 
-          specification='{"maxLength":0,"minLength":4,"pattern":null,"allUpperCase":false,"allLowerCase":false,"propagateSuspension":false,"maxAuthenticationAttempts":3,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":["notpermitted1","notpermitted2"],"suffixesNotPermitted":[]}'/>
-  <Policy DTYPE="SyncPolicy" id="7" description="sync policy 1" type="SYNC" 
-          specification='{"conflictResolutionAction":"IGNORE"}'/>
-  <Policy DTYPE="PasswordPolicy" id="8" description="sample password policy" type="PASSWORD" 
-          specification='{"historyLength":0,"maxLength":0,"minLength":10,"nonAlphanumericRequired":true,"alphanumericRequired":false,"digitRequired":true,"lowercaseRequired":true,"uppercaseRequired":true,"mustStartWithDigit":true,"mustntStartWithDigit":false,"mustEndWithDigit":true,"mustntEndWithDigit":false,"mustStartWithNonAlpha":false,"mustStartWithAlpha":false,"mustntStartWithNonAlpha":false,"mustntStartWithAlpha":false,"mustEndWithNonAlpha":false,"mustEndWithAlpha":false,"mustntEndWithNonAlpha":false,"mustntEndWithAlpha":false,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":["notpermitted1","notpermitted2"],"suffixesNotPermitted":[],"allowNullPassword":false}'/>
-  <Policy DTYPE="SyncPolicy" id="9" description="sync policy for java rule" type="SYNC" 
-          specification='{"conflictResolutionAction":"IGNORE"}'/>
+  <SyncPolicy id="1" description="a sync policy" type="SYNC" 
+              specification='{"conflictResolutionAction":"IGNORE"'/>
+  <PasswordPolicy id="2" description="a password policy" type="PASSWORD" 
+                  historyLength="1" allowNullPassword="1"/> 
+  <PasswordRuleConfInstance id="2" passwordPolicy_id="2"
+                            serializedInstance='{"@class":"org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf","maxLength":0,"minLength":8,"nonAlphanumericRequired":false,"alphanumericRequired":false,"digitRequired":false,"lowercaseRequired":false,"uppercaseRequired":false,"mustStartWithDigit":false,"mustntStartWithDigit":false,"mustEndWithDigit":false,"mustntEndWithDigit":false,"mustStartWithNonAlpha":false,"mustStartWithAlpha":false,"mustntStartWithNonAlpha":false,"mustntStartWithAlpha":false,"mustEndWithNonAlpha":false,"mustEndWithAlpha":false,"mustntEndWithNonAlpha":false,"mustntEndWithAlpha":false,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":["notpermitted1","notpermitted2"],"suffixesNotPermitted":[]}'/>
+  <SyncPolicy id="3" description="sync policy 2" type="SYNC" 
+              specification='{"conflictResolutionAction":"ALL","correlationRules":{"USER":"[\"username\",\"firstname\"]"}}'/>
+  <PasswordPolicy id="4" description="sample password policy" type="PASSWORD"
+                  historyLength="0"  allowNullPassword="1"/> 
+  <PasswordRuleConfInstance id="4" passwordPolicy_id="4"
+                            serializedInstance='{"@class":"org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf","maxLength":0,"minLength":10,"nonAlphanumericRequired":false,"alphanumericRequired":false,"digitRequired":true,"lowercaseRequired":false,"uppercaseRequired":false,"mustStartWithDigit":false,"mustntStartWithDigit":false,"mustEndWithDigit":false,"mustntEndWithDigit":false,"mustStartWithNonAlpha":false,"mustStartWithAlpha":false,"mustntStartWithNonAlpha":false,"mustntStartWithAlpha":false,"mustEndWithNonAlpha":false,"mustEndWithAlpha":false,"mustntEndWithNonAlpha":false,"mustntEndWithAlpha":false,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":["notpermitted1","notpermitted2"],"suffixesNotPermitted":[]}'/>
+  <AccountPolicy id="5" description="an account policy" type="ACCOUNT"
+                 propagateSuspension="0" maxAuthenticationAttempts="0"/>
+  <AccountRuleConfInstance id="5" accountPolicy_id="5"
+                           serializedInstance='{"@class":"org.apache.syncope.common.lib.policy.DefaultAccountRuleConf","maxLength":0,"minLength":0,"pattern":null,"allUpperCase":false,"allLowerCase":false,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":[],"suffixesNotPermitted":[]}'/>
+  <AccountPolicy id="6" description="sample account policy" type="ACCOUNT"
+                 propagateSuspension="0" maxAuthenticationAttempts="3"/>
+  <AccountRuleConfInstance id="6" accountPolicy_id="6"
+                           serializedInstance='{"@class":"org.apache.syncope.common.lib.policy.DefaultAccountRuleConf","maxLength":0,"minLength":4,"pattern":null,"allUpperCase":false,"allLowerCase":false,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":["notpermitted1","notpermitted2"],"suffixesNotPermitted":[]}'/>
+  <SyncPolicy id="7" description="sync policy 1" type="SYNC" 
+              specification='{"conflictResolutionAction":"IGNORE"}'/>
+  <PasswordPolicy id="8" description="sample password policy" type="PASSWORD"
+                  historyLength="0" allowNullPassword="0"/> 
+  <PasswordRuleConfInstance id="8" passwordPolicy_id="8"
+                            serializedInstance='{"@class":"org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf","maxLength":0,"minLength":10,"nonAlphanumericRequired":true,"alphanumericRequired":false,"digitRequired":true,"lowercaseRequired":true,"uppercaseRequired":true,"mustStartWithDigit":true,"mustntStartWithDigit":false,"mustEndWithDigit":true,"mustntEndWithDigit":false,"mustStartWithNonAlpha":false,"mustStartWithAlpha":false,"mustntStartWithNonAlpha":false,"mustntStartWithAlpha":false,"mustEndWithNonAlpha":false,"mustEndWithAlpha":false,"mustntEndWithNonAlpha":false,"mustntEndWithAlpha":false,"wordsNotPermitted":[],"schemasNotPermitted":[],"prefixesNotPermitted":["notpermitted1","notpermitted2"],"suffixesNotPermitted":[]}'/>
+  <SyncPolicy id="9" description="sync policy for java rule" type="SYNC" 
+              specification='{"conflictResolutionAction":"IGNORE"}'/>
 
   <RelationshipType name="inclusion" description="Models the act that an object is included in another"/>
   <RelationshipType name="neighborhood"/>
@@ -698,7 +708,7 @@ under the License.
                     creationDate="2010-10-20 11:00:00" lastChangeDate="2010-10-20 11:00:00"/>
 
   <!-- Use resource-testdb for passthrough authentication (SYNCOPE-164) -->
-  <Policy_ExternalResource accountPolicy_id="5" resource_name="resource-testdb"/>
+  <AccountPolicy_ExternalResource accountPolicy_id="5" resource_name="resource-testdb"/>
     
   <SyncopeUser_ExternalResource user_id="1" resource_name="resource-testdb2"/>
   <SyncopeUser_ExternalResource user_id="3" resource_name="ws-target-resource-delete"/>

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/test/resources/persistenceTest.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/resources/persistenceTest.xml b/core/persistence-jpa/src/test/resources/persistenceTest.xml
index da8f321..2874cac 100644
--- a/core/persistence-jpa/src/test/resources/persistenceTest.xml
+++ b/core/persistence-jpa/src/test/resources/persistenceTest.xml
@@ -49,6 +49,8 @@ under the License.
   <context:component-scan base-package="org.apache.syncope.core.misc.policy"/>
   <context:component-scan base-package="org.apache.syncope.core.misc.security"/>
 
+  <bean class="org.apache.syncope.core.misc.security.DefaultPasswordGenerator"/>
+
   <import resource="persistenceContext.xml"/>
 
 </beans>

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/PolicyDataBinder.java
----------------------------------------------------------------------
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/PolicyDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/PolicyDataBinder.java
index f225b28..aa144f0 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/PolicyDataBinder.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/PolicyDataBinder.java
@@ -18,7 +18,7 @@
  */
 package org.apache.syncope.core.provisioning.api.data;
 
-import org.apache.syncope.common.lib.to.AbstractPolicyTO;
+import org.apache.syncope.common.lib.policy.AbstractPolicyTO;
 import org.apache.syncope.core.persistence.api.entity.Policy;
 
 public interface PolicyDataBinder {

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java
index 7180b6c..f2074f8 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/PolicyDataBinderImpl.java
@@ -20,20 +20,24 @@ package org.apache.syncope.core.provisioning.java.data;
 
 import org.apache.syncope.core.provisioning.api.data.PolicyDataBinder;
 import org.apache.syncope.common.lib.SyncopeClientException;
-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.policy.AbstractAccountRuleConf;
+import org.apache.syncope.common.lib.policy.AbstractPasswordRuleConf;
+import org.apache.syncope.common.lib.policy.AbstractPolicyTO;
+import org.apache.syncope.common.lib.policy.AccountPolicyTO;
+import org.apache.syncope.common.lib.policy.AccountRuleConf;
+import org.apache.syncope.common.lib.policy.PasswordPolicyTO;
+import org.apache.syncope.common.lib.policy.PasswordRuleConf;
+import org.apache.syncope.common.lib.policy.SyncPolicyTO;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
-import org.apache.syncope.core.persistence.api.entity.AccountPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
-import org.apache.syncope.core.persistence.api.entity.PasswordPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.persistence.api.entity.Policy;
 import org.apache.syncope.core.persistence.api.entity.Realm;
-import org.apache.syncope.core.persistence.api.entity.SyncPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.SyncPolicy;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -59,14 +63,33 @@ public class PolicyDataBinderImpl implements PolicyDataBinder {
         T policyTO;
         switch (policy.getType()) {
             case PASSWORD:
-                policyTO = (T) new PasswordPolicyTO();
-                ((PasswordPolicyTO) policyTO).setSpecification(((PasswordPolicy) policy).getSpecification());
+                PasswordPolicy passwordPolicy = PasswordPolicy.class.cast(policy);
+                PasswordPolicyTO passwordPolicyTO = new PasswordPolicyTO();
+                policyTO = (T) passwordPolicyTO;
+
+                passwordPolicyTO.setAllowNullPassword(passwordPolicy.isAllowNullPassword());
+                passwordPolicyTO.setHistoryLength(passwordPolicy.getHistoryLength());
+
+                passwordPolicyTO.getRuleConfs().clear();
+                for (PasswordRuleConf ruleConf : passwordPolicy.getRuleConfs()) {
+                    passwordPolicyTO.getRuleConfs().add((AbstractPasswordRuleConf) ruleConf);
+                }
                 break;
 
             case ACCOUNT:
-                policyTO = (T) new AccountPolicyTO();
-                ((AccountPolicyTO) policyTO).setSpecification(((AccountPolicy) policy).getSpecification());
-                ((AccountPolicyTO) policyTO).getResources().addAll(((AccountPolicy) policy).getResourceNames());
+                AccountPolicy accountPolicy = AccountPolicy.class.cast(policy);
+                AccountPolicyTO accountPolicyTO = new AccountPolicyTO();
+                policyTO = (T) accountPolicyTO;
+
+                accountPolicyTO.setMaxAuthenticationAttempts(accountPolicy.getMaxAuthenticationAttempts());
+                accountPolicyTO.setPropagateSuspension(accountPolicy.isPropagateSuspension());
+
+                accountPolicyTO.getRuleConfs().clear();
+                for (AccountRuleConf ruleConf : accountPolicy.getRuleConfs()) {
+                    accountPolicyTO.getRuleConfs().add((AbstractAccountRuleConf) ruleConf);
+                }
+
+                accountPolicyTO.getResources().addAll(accountPolicy.getResourceNames());
                 break;
 
             case SYNC:
@@ -88,15 +111,6 @@ public class PolicyDataBinderImpl implements PolicyDataBinder {
         return policyTO;
     }
 
-    private ExternalResource getResource(final String resourceName) {
-        ExternalResource resource = resourceDAO.find(resourceName);
-        if (resource == null) {
-            LOG.debug("Ignoring invalid resource {} ", resourceName);
-        }
-
-        return resource;
-    }
-
     @SuppressWarnings("unchecked")
     @Override
     public <T extends Policy> T getPolicy(final T policy, final AbstractPolicyTO policyTO) {
@@ -116,7 +130,16 @@ public class PolicyDataBinderImpl implements PolicyDataBinder {
                 if (result == null) {
                     result = (T) entityFactory.newEntity(PasswordPolicy.class);
                 }
-                ((PasswordPolicy) result).setSpecification(((PasswordPolicyTO) policyTO).getSpecification());
+                PasswordPolicy passwordPolicy = PasswordPolicy.class.cast(result);
+                PasswordPolicyTO passwordPolicyTO = PasswordPolicyTO.class.cast(policyTO);
+
+                passwordPolicy.setAllowNullPassword(passwordPolicyTO.isAllowNullPassword());
+                passwordPolicy.setHistoryLength(passwordPolicyTO.getHistoryLength());
+
+                passwordPolicy.removeAllRuleConfs();
+                for (PasswordRuleConf conf : passwordPolicyTO.getRuleConfs()) {
+                    passwordPolicy.add(conf);
+                }
                 break;
 
             case ACCOUNT:
@@ -127,17 +150,24 @@ public class PolicyDataBinderImpl implements PolicyDataBinder {
                 if (result == null) {
                     result = (T) entityFactory.newEntity(AccountPolicy.class);
                 }
-                ((AccountPolicy) result).setSpecification(((AccountPolicyTO) policyTO).getSpecification());
+                AccountPolicy accountPolicy = AccountPolicy.class.cast(result);
+                AccountPolicyTO accountPolicyTO = AccountPolicyTO.class.cast(policyTO);
+
+                accountPolicy.setMaxAuthenticationAttempts(accountPolicyTO.getMaxAuthenticationAttempts());
+                accountPolicy.setPropagateSuspension(accountPolicyTO.isPropagateSuspension());
 
-                if (((AccountPolicy) result).getResources() != null
-                        && !((AccountPolicy) result).getResources().isEmpty()) {
-                    ((AccountPolicy) result).getResources().clear();
+                accountPolicy.removeAllRuleConfs();
+                for (AccountRuleConf conf : accountPolicyTO.getRuleConfs()) {
+                    accountPolicy.add(conf);
                 }
-                for (String resourceName : ((AccountPolicyTO) policyTO).getResources()) {
-                    ExternalResource resource = getResource(resourceName);
 
-                    if (resource != null) {
-                        ((AccountPolicy) result).add(resource);
+                accountPolicy.getResources().clear();
+                for (String resourceName : accountPolicyTO.getResources()) {
+                    ExternalResource resource = resourceDAO.find(resourceName);
+                    if (resource == null) {
+                        LOG.debug("Ignoring invalid resource {} ", resourceName);
+                    } else {
+                        accountPolicy.add(resource);
                     }
                 }
                 break;

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderImpl.java
index a5f12aa..d9a2838 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderImpl.java
@@ -21,9 +21,9 @@ package org.apache.syncope.core.provisioning.java.data;
 import org.apache.syncope.common.lib.to.RealmTO;
 import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
-import org.apache.syncope.core.persistence.api.entity.AccountPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
-import org.apache.syncope.core.persistence.api.entity.PasswordPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.provisioning.api.data.RealmDataBinder;
 import org.springframework.beans.factory.annotation.Autowired;

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ReportDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ReportDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ReportDataBinderImpl.java
index 6a40803..252b719 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ReportDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ReportDataBinderImpl.java
@@ -18,10 +18,9 @@
  */
 package org.apache.syncope.core.provisioning.java.data;
 
-import java.util.HashSet;
-import java.util.Set;
-import org.apache.syncope.core.provisioning.api.data.ReportDataBinder;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.report.AbstractReportletConf;
+import org.apache.syncope.core.provisioning.api.data.ReportDataBinder;
 import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.common.lib.to.ReportExecTO;
 import org.apache.syncope.common.lib.to.ReportTO;
@@ -43,9 +42,6 @@ import org.springframework.stereotype.Component;
 @Component
 public class ReportDataBinderImpl implements ReportDataBinder {
 
-    /**
-     * Logger.
-     */
     private static final Logger LOG = LoggerFactory.getLogger(ReportDataBinder.class);
 
     private static final String[] IGNORE_REPORT_PROPERTIES = { "key", "reportlets", "executions" };
@@ -62,16 +58,7 @@ public class ReportDataBinderImpl implements ReportDataBinder {
     public void getReport(final Report report, final ReportTO reportTO) {
         BeanUtils.copyProperties(reportTO, report, IGNORE_REPORT_PROPERTIES);
 
-        // 1. remove all reportlet confs
-        Set<ReportletConf> toRemove = new HashSet<>();
-        for (ReportletConf conf : report.getReportletConfs()) {
-            toRemove.add(conf);
-        }
-        for (ReportletConf conf : toRemove) {
-            report.remove(conf);
-        }
-
-        // 2. take all reportlet confs from reportTO
+        report.removeAllReportletConfs();
         for (ReportletConf conf : reportTO.getReportletConfs()) {
             report.add(conf);
         }
@@ -83,11 +70,14 @@ public class ReportDataBinderImpl implements ReportDataBinder {
         reportTO.setKey(report.getKey());
         BeanUtils.copyProperties(report, reportTO, IGNORE_REPORT_PROPERTIES);
 
-        copyReportletConfs(report, reportTO);
+        reportTO.getReportletConfs().clear();
+        for (ReportletConf reportletConf : report.getReportletConfs()) {
+            reportTO.getReportletConfs().add((AbstractReportletConf) reportletConf);
+        }
 
         ReportExec latestExec = reportExecDAO.findLatestStarted(report);
         reportTO.setLatestExecStatus(latestExec == null
-                ? ""
+                ? StringUtils.EMPTY
                 : latestExec.getStatus());
 
         reportTO.setStartDate(latestExec == null
@@ -120,13 +110,6 @@ public class ReportDataBinderImpl implements ReportDataBinder {
         return reportTO;
     }
 
-    private void copyReportletConfs(final Report report, final ReportTO reportTO) {
-        reportTO.getReportletConfs().clear();
-        for (ReportletConf reportletConf : report.getReportletConfs()) {
-            reportTO.getReportletConfs().add((AbstractReportletConf) reportletConf);
-        }
-    }
-
     @Override
     public ReportExecTO getReportExecTO(final ReportExec execution) {
         ReportExecTO executionTO = new ReportExecTO();

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
index cb8f979..5821106 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
@@ -36,14 +36,14 @@ import org.apache.syncope.common.lib.types.IntMappingType;
 import org.apache.syncope.core.persistence.api.dao.ConnInstanceDAO;
 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.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.ConnInstance;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.resource.Mapping;
 import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
-import org.apache.syncope.core.persistence.api.entity.PasswordPolicy;
-import org.apache.syncope.core.persistence.api.entity.SyncPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.SyncPolicy;
 import org.apache.syncope.core.provisioning.api.ConnectorRegistry;
 import org.apache.syncope.core.misc.jexl.JexlUtils;
 import org.slf4j.Logger;

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncJobDelegate.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncJobDelegate.java
index 2e70e80..3b9dc70 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncJobDelegate.java
@@ -22,7 +22,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.lib.types.SyncPolicySpec;
+import org.apache.syncope.common.lib.policy.SyncPolicySpec;
 import org.apache.syncope.core.misc.spring.ApplicationContextProvider;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncUtils.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncUtils.java
index 4878362..642181f 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncUtils.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/sync/SyncUtils.java
@@ -25,7 +25,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.MappingPurpose;
-import org.apache.syncope.common.lib.types.SyncPolicySpec;
+import org.apache.syncope.common.lib.policy.SyncPolicySpec;
 import org.apache.syncope.core.misc.MappingUtils;
 import org.apache.syncope.core.misc.serialization.POJOHelper;
 import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException;

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/PolicyServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/PolicyServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/PolicyServiceImpl.java
index ecec019..a6c4c5d 100644
--- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/PolicyServiceImpl.java
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/PolicyServiceImpl.java
@@ -21,10 +21,10 @@ package org.apache.syncope.core.rest.cxf.service;
 import java.net.URI;
 import java.util.List;
 import javax.ws.rs.core.Response;
-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.policy.AbstractPolicyTO;
+import org.apache.syncope.common.lib.policy.AccountPolicyTO;
+import org.apache.syncope.common.lib.policy.PasswordPolicyTO;
+import org.apache.syncope.common.lib.policy.SyncPolicyTO;
 import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.syncope.common.rest.api.RESTHeaders;
 import org.apache.syncope.common.rest.api.service.PolicyService;

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReportServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReportServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReportServiceImpl.java
index 9fa3d99..e04053a 100644
--- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReportServiceImpl.java
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ReportServiceImpl.java
@@ -31,8 +31,6 @@ import org.apache.syncope.common.lib.to.ReportTO;
 import org.apache.syncope.common.lib.types.JobAction;
 import org.apache.syncope.common.lib.types.JobStatusType;
 import org.apache.syncope.common.lib.types.ReportExecExportFormat;
-import org.apache.syncope.common.lib.wrap.ReportletConfClass;
-import org.apache.syncope.common.rest.api.CollectionWrapper;
 import org.apache.syncope.common.rest.api.RESTHeaders;
 import org.apache.syncope.common.rest.api.beans.ListQuery;
 import org.apache.syncope.common.rest.api.service.ReportService;
@@ -74,11 +72,6 @@ public class ReportServiceImpl extends AbstractServiceImpl implements ReportServ
     }
 
     @Override
-    public List<ReportletConfClass> getReportletConfClasses() {
-        return CollectionWrapper.wrap(logic.getReportletConfClasses(), ReportletConfClass.class);
-    }
-
-    @Override
     public ReportTO read(final Long key) {
         return logic.read(key);
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestAccountRuleConf.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestAccountRuleConf.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestAccountRuleConf.java
index 8903058..47c468d 100644
--- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestAccountRuleConf.java
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestAccountRuleConf.java
@@ -18,19 +18,20 @@
  */
 package org.apache.syncope.fit.core.reference;
 
-import org.apache.syncope.common.lib.AbstractBaseBean;
-import org.apache.syncope.common.lib.policy.AccountRuleConf;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.syncope.common.lib.policy.AbstractAccountRuleConf;
 
-public class TestAccountRuleConf extends AbstractBaseBean implements AccountRuleConf {
+@XmlRootElement(name = "testAccountRuleConf")
+@XmlType
+public class TestAccountRuleConf extends AbstractAccountRuleConf {
 
     private static final long serialVersionUID = -1803947511928491978L;
 
-    @Override
-    public String getName() {
-        return getClass().getName();
-    }
+    private String mustContainSubstring = "YYY";
 
     public String getMustContainSubstring() {
-        return "YYY";
+        return mustContainSubstring;
     }
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPasswordRule.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPasswordRule.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPasswordRule.java
index 2b67f37..ea41274 100644
--- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPasswordRule.java
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPasswordRule.java
@@ -18,30 +18,30 @@
  */
 package org.apache.syncope.fit.core.reference;
 
-import org.apache.syncope.common.lib.policy.AccountRuleConf;
-import org.apache.syncope.core.misc.policy.AccountPolicyException;
-import org.apache.syncope.core.persistence.api.dao.AccountRule;
-import org.apache.syncope.core.persistence.api.dao.AccountRuleConfClass;
+import org.apache.syncope.common.lib.policy.PasswordRuleConf;
+import org.apache.syncope.core.misc.policy.PasswordPolicyException;
+import org.apache.syncope.core.persistence.api.dao.PasswordRule;
+import org.apache.syncope.core.persistence.api.dao.PasswordRuleConfClass;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.springframework.transaction.annotation.Transactional;
 
-@AccountRuleConfClass(TestAccountRuleConf.class)
-public class TestAccountRule implements AccountRule {
+@PasswordRuleConfClass(TestPasswordRuleConf.class)
+public class TestPasswordRule implements PasswordRule {
 
-    private TestAccountRuleConf conf;
+    private TestPasswordRuleConf conf;
 
     @Transactional(readOnly = true)
     @Override
-    public void enforce(final AccountRuleConf conf, final User user) {
-        if (conf instanceof TestAccountRuleConf) {
-            this.conf = TestAccountRuleConf.class.cast(conf);
+    public void enforce(final PasswordRuleConf conf, final User user) {
+        if (conf instanceof TestPasswordRuleConf) {
+            this.conf = TestPasswordRuleConf.class.cast(conf);
         } else {
             throw new IllegalArgumentException(
-                    AccountRuleConf.class.getName() + " expected, got " + conf.getClass().getName());
+                    PasswordRuleConf.class.getName() + " expected, got " + conf.getClass().getName());
         }
 
-        if (!user.getUsername().contains(this.conf.getMustContainSubstring())) {
-            throw new AccountPolicyException("Username not containing " + this.conf.getMustContainSubstring());
+        if (!user.getClearPassword().endsWith(this.conf.getMustEndWith())) {
+            throw new PasswordPolicyException("Password not ending with " + this.conf.getMustEndWith());
         }
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPasswordRuleConf.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPasswordRuleConf.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPasswordRuleConf.java
index 8903058..c6c6446 100644
--- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPasswordRuleConf.java
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestPasswordRuleConf.java
@@ -18,19 +18,20 @@
  */
 package org.apache.syncope.fit.core.reference;
 
-import org.apache.syncope.common.lib.AbstractBaseBean;
-import org.apache.syncope.common.lib.policy.AccountRuleConf;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.syncope.common.lib.policy.AbstractPasswordRuleConf;
 
-public class TestAccountRuleConf extends AbstractBaseBean implements AccountRuleConf {
+@XmlRootElement(name = "testPasswordRuleConf")
+@XmlType
+public class TestPasswordRuleConf extends AbstractPasswordRuleConf {
 
     private static final long serialVersionUID = -1803947511928491978L;
 
-    @Override
-    public String getName() {
-        return getClass().getName();
-    }
+    private String mustEndWith = "XXX";
 
-    public String getMustContainSubstring() {
-        return "YYY";
+    public String getMustEndWith() {
+        return mustEndWith;
     }
+
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/fit/core-reference/src/main/resources/log4j2.xml
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/log4j2.xml b/fit/core-reference/src/main/resources/log4j2.xml
index df1de40..7ef0997 100644
--- a/fit/core-reference/src/main/resources/log4j2.xml
+++ b/fit/core-reference/src/main/resources/log4j2.xml
@@ -103,7 +103,7 @@ under the License.
       <appender-ref ref="connid"/>
     </asyncLogger>
     
-    <asyncLogger name="org.apache.syncope" additivity="false" level="ERROR">
+    <asyncLogger name="org.apache.syncope" additivity="false" level="INFO">
       <appender-ref ref="main"/>
     </asyncLogger>
     <asyncLogger name="org.apache.syncope.core.provisioning" additivity="false" level="INFO">

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AbstractITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AbstractITCase.java
index 871155a..368035e 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AbstractITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AbstractITCase.java
@@ -39,7 +39,7 @@ import org.apache.syncope.common.lib.mod.AnyObjectMod;
 import org.apache.syncope.common.lib.mod.AttrMod;
 import org.apache.syncope.common.lib.mod.GroupMod;
 import org.apache.syncope.common.lib.mod.UserMod;
-import org.apache.syncope.common.lib.to.AbstractPolicyTO;
+import org.apache.syncope.common.lib.policy.AbstractPolicyTO;
 import org.apache.syncope.common.lib.to.AbstractSchemaTO;
 import org.apache.syncope.common.lib.to.AnyObjectTO;
 import org.apache.syncope.common.lib.to.AttrTO;

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/PolicyITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/PolicyITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/PolicyITCase.java
index 95701ae..a08b881 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/PolicyITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/PolicyITCase.java
@@ -20,22 +20,24 @@ package org.apache.syncope.fit.core.reference;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import java.util.Arrays;
 import java.util.List;
+import org.apache.commons.lang3.SerializationUtils;
 import org.apache.syncope.common.lib.SyncopeClientException;
-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.AccountPolicySpec;
+import org.apache.syncope.common.lib.policy.AccountPolicyTO;
+import org.apache.syncope.common.lib.policy.PasswordPolicyTO;
+import org.apache.syncope.common.lib.policy.SyncPolicyTO;
+import org.apache.syncope.common.lib.policy.DefaultAccountRuleConf;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
-import org.apache.syncope.common.lib.types.PasswordPolicySpec;
+import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf;
 import org.apache.syncope.common.lib.types.PolicyType;
-import org.apache.syncope.common.lib.types.SyncPolicySpec;
+import org.apache.syncope.common.lib.policy.SyncPolicySpec;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
 import org.junit.runners.MethodSorters;
@@ -116,24 +118,17 @@ public class PolicyITCase extends AbstractITCase {
 
     @Test
     public void update() {
-        // get global password
         PasswordPolicyTO globalPolicy = policyService.read(2L);
 
-        PasswordPolicyTO policy = new PasswordPolicyTO();
+        PasswordPolicyTO policy = SerializationUtils.clone(globalPolicy);
         policy.setDescription("A simple password policy");
-        policy.setSpecification(globalPolicy.getSpecification());
 
-        // create a new password policy using global password as a template
+        // create a new password policy using the former as a template
         policy = createPolicy(policy);
+        assertNotNull(policy);
+        assertNotEquals(2L, policy.getKey());
 
-        // read new password policy
-        policy = policyService.read(policy.getKey());
-
-        assertNotNull("find to update did not work", policy);
-
-        PasswordPolicySpec policySpec = policy.getSpecification();
-        policySpec.setMaxLength(22);
-        policy.setSpecification(policySpec);
+        ((DefaultPasswordRuleConf) policy.getRuleConfs().get(0)).setMaxLength(22);
 
         // update new password policy
         policyService.update(policy);
@@ -141,8 +136,8 @@ public class PolicyITCase extends AbstractITCase {
 
         assertNotNull(policy);
         assertEquals(PolicyType.PASSWORD, policy.getType());
-        assertEquals(22, policy.getSpecification().getMaxLength());
-        assertEquals(8, policy.getSpecification().getMinLength());
+        assertEquals(22, ((DefaultPasswordRuleConf) policy.getRuleConfs().get(0)).getMaxLength());
+        assertEquals(8, ((DefaultPasswordRuleConf) policy.getRuleConfs().get(0)).getMinLength());
     }
 
     @Test
@@ -172,10 +167,10 @@ public class PolicyITCase extends AbstractITCase {
         AccountPolicyTO policy = new AccountPolicyTO();
         policy.setDescription("SYNCOPE553");
 
-        final AccountPolicySpec accountPolicySpec = new AccountPolicySpec();
-        accountPolicySpec.setMinLength(3);
-        accountPolicySpec.setMaxLength(8);
-        policy.setSpecification(accountPolicySpec);
+        DefaultAccountRuleConf ruleConf = new DefaultAccountRuleConf();
+        ruleConf.setMinLength(3);
+        ruleConf.setMaxLength(8);
+        policy.getRuleConfs().add(ruleConf);
 
         policy = createPolicy(policy);
         assertNotNull(policy);
@@ -187,10 +182,10 @@ public class PolicyITCase extends AbstractITCase {
         policy.setDescription("SYNCOPE682");
         policy.getResources().add(RESOURCE_NAME_LDAP);
 
-        final AccountPolicySpec accountPolicySpec = new AccountPolicySpec();
-        accountPolicySpec.setMinLength(3);
-        accountPolicySpec.setMaxLength(8);
-        policy.setSpecification(accountPolicySpec);
+        DefaultAccountRuleConf ruleConf = new DefaultAccountRuleConf();
+        ruleConf.setMinLength(3);
+        ruleConf.setMaxLength(8);
+        policy.getRuleConfs().add(ruleConf);
 
         policy = createPolicy(policy);
         assertNotNull(policy);

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/RealmITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/RealmITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/RealmITCase.java
index fe2c0a5..c203272 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/RealmITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/RealmITCase.java
@@ -31,9 +31,9 @@ import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Predicate;
 import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.SyncopeConstants;
-import org.apache.syncope.common.lib.to.AccountPolicyTO;
+import org.apache.syncope.common.lib.policy.AccountPolicyTO;
 import org.apache.syncope.common.lib.to.RealmTO;
-import org.apache.syncope.common.lib.types.AccountPolicySpec;
+import org.apache.syncope.common.lib.policy.DefaultAccountRuleConf;
 import org.apache.syncope.common.lib.types.ClientExceptionType;
 import org.apache.syncope.common.rest.api.service.RealmService;
 import org.junit.FixMethodOrder;
@@ -136,10 +136,10 @@ public class RealmITCase extends AbstractITCase {
         AccountPolicyTO policy = new AccountPolicyTO();
         policy.setDescription("deletingAccountPolicy");
 
-        final AccountPolicySpec accountPolicySpec = new AccountPolicySpec();
-        accountPolicySpec.setMinLength(3);
-        accountPolicySpec.setMaxLength(8);
-        policy.setSpecification(accountPolicySpec);
+        DefaultAccountRuleConf ruleConf = new DefaultAccountRuleConf();
+        ruleConf.setMinLength(3);
+        ruleConf.setMaxLength(8);
+        policy.getRuleConfs().add(ruleConf);
 
         policy = createPolicy(policy);
         assertNotNull(policy);

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/ReportITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/ReportITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/ReportITCase.java
index d02b726..e092a1d 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/ReportITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/ReportITCase.java
@@ -39,7 +39,6 @@ import org.apache.syncope.common.lib.to.ReportExecTO;
 import org.apache.syncope.common.lib.to.ReportTO;
 import org.apache.syncope.common.lib.types.ReportExecExportFormat;
 import org.apache.syncope.common.lib.types.ReportExecStatus;
-import org.apache.syncope.common.lib.wrap.ReportletConfClass;
 import org.apache.syncope.common.rest.api.service.ReportService;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -56,9 +55,9 @@ public class ReportITCase extends AbstractITCase {
 
     @Test
     public void getReportletClasses() {
-        List<ReportletConfClass> reportletClasses = reportService.getReportletConfClasses();
-        assertNotNull(reportletClasses);
-        assertFalse(reportletClasses.isEmpty());
+        List<String> reportlets = syncopeService.info().getReportlets();
+        assertNotNull(reportlets);
+        assertFalse(reportlets.isEmpty());
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java
index d97eaed..4604731 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java
@@ -46,7 +46,7 @@ import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.lib.to.ResourceTO;
 import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.common.lib.to.ProvisionTO;
-import org.apache.syncope.common.lib.to.SyncPolicyTO;
+import org.apache.syncope.common.lib.policy.SyncPolicyTO;
 import org.apache.syncope.common.lib.to.SyncTaskTO;
 import org.apache.syncope.common.lib.to.TaskExecTO;
 import org.apache.syncope.common.lib.to.UserTO;

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/UserITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/UserITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/UserITCase.java
index da438d3..5fe876f 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/UserITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/UserITCase.java
@@ -38,6 +38,7 @@ import java.util.Map;
 import java.util.Set;
 import javax.naming.NamingException;
 import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
@@ -50,6 +51,9 @@ import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.mod.ResourceAssociationMod;
 import org.apache.syncope.common.lib.mod.StatusMod;
 import org.apache.syncope.common.lib.mod.UserMod;
+import org.apache.syncope.common.lib.policy.AccountPolicyTO;
+import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf;
+import org.apache.syncope.common.lib.policy.PasswordPolicyTO;
 import org.apache.syncope.common.lib.to.AttrTO;
 import org.apache.syncope.common.lib.to.BulkAction;
 import org.apache.syncope.common.lib.to.BulkActionResult;
@@ -63,6 +67,7 @@ import org.apache.syncope.common.lib.to.PropagationStatus;
 import org.apache.syncope.common.lib.to.PropagationTaskTO;
 import org.apache.syncope.common.lib.to.ResourceTO;
 import org.apache.syncope.common.lib.to.GroupTO;
+import org.apache.syncope.common.lib.to.RealmTO;
 import org.apache.syncope.common.lib.to.UserTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.CipherAlgorithm;
@@ -1162,6 +1167,66 @@ public class UserITCase extends AbstractITCase {
     }
 
     @Test
+    public void customPolicyRules() {
+        // Using custom policy rules with application/xml requires to overwrite
+        // org.apache.syncope.common.lib.policy.AbstractAccountRuleConf's and / or
+        // org.apache.syncope.common.lib.policy.AbstractPasswordRuleConf's
+        // @XmlSeeAlso - the power of JAXB :-/
+        Assume.assumeTrue(MediaType.APPLICATION_JSON_TYPE.equals(clientFactory.getContentType().getMediaType()));
+
+        AccountPolicyTO accountPolicy = new AccountPolicyTO();
+        accountPolicy.setDescription("Account Policy with custom rules");
+        accountPolicy.getRuleConfs().add(new TestAccountRuleConf());
+        accountPolicy = createPolicy(accountPolicy);
+        assertNotNull(accountPolicy);
+
+        PasswordPolicyTO passwordPolicy = new PasswordPolicyTO();
+        passwordPolicy.setDescription("Password Policy with custom rules");
+        passwordPolicy.getRuleConfs().add(new TestPasswordRuleConf());
+        passwordPolicy = createPolicy(passwordPolicy);
+        assertNotNull(passwordPolicy);
+
+        RealmTO realm = realmService.list("/even/two").get(0);
+        Long oldAccountPolicy = realm.getAccountPolicy();
+        realm.setAccountPolicy(accountPolicy.getKey());
+        Long oldPasswordPolicy = realm.getPasswordPolicy();
+        realm.setPasswordPolicy(passwordPolicy.getKey());
+        realmService.update(realm);
+
+        try {
+            UserTO user = getUniqueSampleTO("custompolicyrules@syncope.apache.org");
+            user.setRealm(realm.getFullPath());
+            try {
+                createUser(user);
+                fail();
+            } catch (SyncopeClientException e) {
+                assertEquals(ClientExceptionType.InvalidUser, e.getType());
+                assertTrue(e.getElements().iterator().next().startsWith("InvalidPassword"));
+            }
+
+            user.setPassword(user.getPassword() + "XXX");
+            try {
+                createUser(user);
+                fail();
+            } catch (SyncopeClientException e) {
+                assertEquals(ClientExceptionType.InvalidUser, e.getType());
+                assertTrue(e.getElements().iterator().next().startsWith("InvalidUsername"));
+            }
+
+            user.setUsername("YYY" + user.getUsername());
+            user = createUser(user);
+            assertNotNull(user);
+        } finally {
+            realm.setAccountPolicy(oldAccountPolicy);
+            realm.setPasswordPolicy(oldPasswordPolicy);
+            realmService.update(realm);
+
+            policyService.delete(passwordPolicy.getKey());
+            policyService.delete(accountPolicy.getKey());
+        }
+    }
+
+    @Test
     public void issueSYNCOPE108() {
         UserTO userTO = getUniqueSampleTO("syncope108@syncope.apache.org");
         userTO.getResources().clear();
@@ -2418,4 +2483,45 @@ public class UserITCase extends AbstractITCase {
         assertNotNull(connObjectTO);
         assertEquals("newPostalAddress", connObjectTO.getPlainAttrMap().get("postalAddress").getValues().get(0));
     }
+
+    @Test
+    public void issueSYNCOPE626() {
+        PasswordPolicyTO passwordPolicy = new PasswordPolicyTO();
+        passwordPolicy.setDescription("Password Policy for SYNCOPE-626");
+
+        DefaultPasswordRuleConf ruleConf = new DefaultPasswordRuleConf();
+        ruleConf.setUsernameAllowed(false);
+        passwordPolicy.getRuleConfs().add(ruleConf);
+
+        passwordPolicy = createPolicy(passwordPolicy);
+        assertNotNull(passwordPolicy);
+
+        RealmTO realm = realmService.list("/even/two").get(0);
+        Long oldPasswordPolicy = realm.getPasswordPolicy();
+        realm.setPasswordPolicy(passwordPolicy.getKey());
+        realmService.update(realm);
+
+        try {
+            UserTO user = getUniqueSampleTO("syncope626@syncope.apache.org");
+            user.setRealm(realm.getFullPath());
+            user.setPassword(user.getUsername());
+            try {
+                createUser(user);
+                fail();
+            } catch (SyncopeClientException e) {
+                assertEquals(ClientExceptionType.InvalidUser, e.getType());
+                assertTrue(e.getElements().iterator().next().startsWith("InvalidPassword"));
+            }
+
+            user.setPassword("password123");
+            user = createUser(user);
+            assertNotNull(user);
+        } finally {
+            realm.setPasswordPolicy(oldPasswordPolicy);
+            realmService.update(realm);
+
+            policyService.delete(passwordPolicy.getKey());
+        }
+
+    }
 }


[14/33] syncope git commit: [SYNCOPE-685] Now account and password policies can be composed of several 'rules', where each rule can be provided as a separate Java class (similar to Reportlet mechanism); previous account and password policies are now impl

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

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/AccountPolicy.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/AccountPolicy.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/AccountPolicy.java
index f9a8277..066efdf 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/AccountPolicy.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/AccountPolicy.java
@@ -36,7 +36,7 @@ public interface AccountPolicy extends Policy {
 
     boolean add(AccountRuleConf accountRuleConf);
 
-    boolean remove(AccountRuleConf accountRuleConf);
+    void removeAllRuleConfs();
 
     List<AccountRuleConf> getRuleConfs();
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PasswordPolicy.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PasswordPolicy.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PasswordPolicy.java
index 173608e..a3e565f 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PasswordPolicy.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/policy/PasswordPolicy.java
@@ -34,7 +34,7 @@ public interface PasswordPolicy extends Policy {
 
     boolean add(PasswordRuleConf passwordRuleConf);
 
-    boolean remove(PasswordRuleConf passwordRuleConf);
+    void removeAllRuleConfs();
 
     List<PasswordRuleConf> getRuleConfs();
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/resource/ExternalResource.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/resource/ExternalResource.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/resource/ExternalResource.java
index 63047a1..a8f32c0 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/resource/ExternalResource.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/resource/ExternalResource.java
@@ -23,12 +23,12 @@ import java.util.Set;
 import org.apache.syncope.common.lib.types.ConnConfProperty;
 import org.apache.syncope.common.lib.types.PropagationMode;
 import org.apache.syncope.common.lib.types.TraceLevel;
-import org.apache.syncope.core.persistence.api.entity.AccountPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.AnnotatedEntity;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.ConnInstance;
-import org.apache.syncope.core.persistence.api.entity.PasswordPolicy;
-import org.apache.syncope.core.persistence.api.entity.SyncPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.SyncPolicy;
 import org.identityconnectors.framework.common.objects.ObjectClass;
 
 public interface ExternalResource extends AnnotatedEntity<String> {

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultAccountRule.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultAccountRule.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultAccountRule.java
index 7ef2ebf..d4eaf63 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultAccountRule.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultAccountRule.java
@@ -36,25 +36,22 @@ public class DefaultAccountRule implements AccountRule {
 
     private DefaultAccountRuleConf conf;
 
+    @Transactional(readOnly = true)
     @Override
-    public void setConf(final AccountRuleConf conf) {
+    public void enforce(final AccountRuleConf conf, final User user) {
         if (conf instanceof DefaultAccountRuleConf) {
-            this.conf = (DefaultAccountRuleConf) conf;
+            this.conf = DefaultAccountRuleConf.class.cast(conf);
         } else {
             throw new IllegalArgumentException(
                     AccountRuleConf.class.getName() + " expected, got " + conf.getClass().getName());
         }
-    }
 
-    @Transactional(readOnly = true)
-    @Override
-    public void isCompliant(final User user) {
-        for (String schema : conf.getSchemasNotPermitted()) {
+        for (String schema : this.conf.getSchemasNotPermitted()) {
             PlainAttr<?> attr = user.getPlainAttr(schema);
             if (attr != null) {
                 List<String> values = attr.getValuesAsStrings();
                 if (values != null && !values.isEmpty()) {
-                    conf.getWordsNotPermitted().add(values.get(0));
+                    this.conf.getWordsNotPermitted().add(values.get(0));
                 }
             }
         }
@@ -64,45 +61,45 @@ public class DefaultAccountRule implements AccountRule {
         }
 
         // check min length
-        if (conf.getMinLength() > 0 && conf.getMinLength() > user.getUsername().length()) {
+        if (this.conf.getMinLength() > 0 && this.conf.getMinLength() > user.getUsername().length()) {
             throw new AccountPolicyException("Username too short");
         }
 
         // check max length
-        if (conf.getMaxLength() > 0 && conf.getMaxLength() < user.getUsername().length()) {
+        if (this.conf.getMaxLength() > 0 && this.conf.getMaxLength() < user.getUsername().length()) {
             throw new AccountPolicyException("Username too long");
         }
 
         // check words not permitted
-        for (String word : conf.getWordsNotPermitted()) {
+        for (String word : this.conf.getWordsNotPermitted()) {
             if (user.getUsername().contains(word)) {
                 throw new AccountPolicyException("Used word(s) not permitted");
             }
         }
 
         // check case
-        if (conf.isAllUpperCase() && !user.getUsername().equals(user.getUsername().toUpperCase())) {
+        if (this.conf.isAllUpperCase() && !user.getUsername().equals(user.getUsername().toUpperCase())) {
             throw new AccountPolicyException("No lowercase characters permitted");
         }
-        if (conf.isAllLowerCase() && !user.getUsername().equals(user.getUsername().toLowerCase())) {
+        if (this.conf.isAllLowerCase() && !user.getUsername().equals(user.getUsername().toLowerCase())) {
             throw new AccountPolicyException("No uppercase characters permitted");
         }
 
         // check pattern
-        Pattern pattern = (conf.getPattern() == null) ? DEFAULT_PATTERN : Pattern.compile(conf.getPattern());
+        Pattern pattern = (this.conf.getPattern() == null) ? DEFAULT_PATTERN : Pattern.compile(this.conf.getPattern());
         if (!pattern.matcher(user.getUsername()).matches()) {
             throw new AccountPolicyException("Username does not match pattern");
         }
 
         // check prefix
-        for (String prefix : conf.getPrefixesNotPermitted()) {
+        for (String prefix : this.conf.getPrefixesNotPermitted()) {
             if (user.getUsername().startsWith(prefix)) {
                 throw new AccountPolicyException("Prefix not permitted");
             }
         }
 
         // check suffix
-        for (String suffix : conf.getSuffixesNotPermitted()) {
+        for (String suffix : this.conf.getSuffixesNotPermitted()) {
             if (user.getUsername().endsWith(suffix)) {
                 throw new AccountPolicyException("Suffix not permitted");
             }

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPasswordRule.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPasswordRule.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPasswordRule.java
index 8eaad0e..d3ab592 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPasswordRule.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/DefaultPasswordRule.java
@@ -34,25 +34,22 @@ public class DefaultPasswordRule implements PasswordRule {
 
     private DefaultPasswordRuleConf conf;
 
+    @Transactional(readOnly = true)
     @Override
-    public void setConf(final PasswordRuleConf conf) {
+    public void enforce(final PasswordRuleConf conf, final User user) {
         if (conf instanceof DefaultPasswordRuleConf) {
             this.conf = (DefaultPasswordRuleConf) conf;
         } else {
             throw new IllegalArgumentException(
                     PasswordRuleConf.class.getName() + " expected, got " + conf.getClass().getName());
         }
-    }
 
-    @Transactional(readOnly = true)
-    @Override
-    public void isCompliant(final User user) {
-        for (String schema : conf.getSchemasNotPermitted()) {
+        for (String schema : this.conf.getSchemasNotPermitted()) {
             PlainAttr<?> attr = user.getPlainAttr(schema);
             if (attr != null) {
                 List<String> values = attr.getValuesAsStrings();
                 if (values != null && !values.isEmpty()) {
-                    conf.getWordsNotPermitted().add(values.get(0));
+                    this.conf.getWordsNotPermitted().add(values.get(0));
                 }
             }
         }
@@ -62,113 +59,119 @@ public class DefaultPasswordRule implements PasswordRule {
 
         if (password != null && clearPassword != null) {
             // check length
-            if (conf.getMinLength() > 0 && conf.getMinLength() > clearPassword.length()) {
+            if (this.conf.getMinLength() > 0 && this.conf.getMinLength() > clearPassword.length()) {
                 throw new PasswordPolicyException("Password too short");
             }
 
-            if (conf.getMaxLength() > 0 && conf.getMaxLength() < clearPassword.length()) {
+            if (this.conf.getMaxLength() > 0 && this.conf.getMaxLength() < clearPassword.length()) {
                 throw new PasswordPolicyException("Password too long");
             }
 
             // check words not permitted
-            for (String word : conf.getWordsNotPermitted()) {
+            for (String word : this.conf.getWordsNotPermitted()) {
                 if (clearPassword.contains(word)) {
                     throw new PasswordPolicyException("Used word(s) not permitted");
                 }
             }
 
             // check digits occurrence
-            if (conf.isDigitRequired() && !checkDigit(clearPassword)) {
+            if (this.conf.isDigitRequired() && !checkDigit(clearPassword)) {
                 throw new PasswordPolicyException("Password must contain digit(s)");
             }
 
             // check lowercase alphabetic characters occurrence
-            if (conf.isLowercaseRequired() && !checkLowercase(clearPassword)) {
+            if (this.conf.isLowercaseRequired() && !checkLowercase(clearPassword)) {
                 throw new PasswordPolicyException("Password must contain lowercase alphabetic character(s)");
             }
 
             // check uppercase alphabetic characters occurrence
-            if (conf.isUppercaseRequired() && !checkUppercase(clearPassword)) {
+            if (this.conf.isUppercaseRequired() && !checkUppercase(clearPassword)) {
                 throw new PasswordPolicyException("Password must contain uppercase alphabetic character(s)");
             }
 
             // check prefix
-            for (String prefix : conf.getPrefixesNotPermitted()) {
+            for (String prefix : this.conf.getPrefixesNotPermitted()) {
                 if (clearPassword.startsWith(prefix)) {
                     throw new PasswordPolicyException("Prefix not permitted");
                 }
             }
 
             // check suffix
-            for (String suffix : conf.getSuffixesNotPermitted()) {
+            for (String suffix : this.conf.getSuffixesNotPermitted()) {
                 if (clearPassword.endsWith(suffix)) {
                     throw new PasswordPolicyException("Suffix not permitted");
                 }
             }
 
             // check digit first occurrence
-            if (conf.isMustStartWithDigit() && !checkFirstDigit(clearPassword)) {
+            if (this.conf.isMustStartWithDigit() && !checkFirstDigit(clearPassword)) {
                 throw new PasswordPolicyException("Password must start with a digit");
             }
 
-            if (conf.isMustntStartWithDigit() && checkFirstDigit(clearPassword)) {
+            if (this.conf.isMustntStartWithDigit() && checkFirstDigit(clearPassword)) {
                 throw new PasswordPolicyException("Password mustn't start with a digit");
             }
 
             // check digit last occurrence
-            if (conf.isMustEndWithDigit() && !checkLastDigit(clearPassword)) {
+            if (this.conf.isMustEndWithDigit() && !checkLastDigit(clearPassword)) {
                 throw new PasswordPolicyException("Password must end with a digit");
             }
 
-            if (conf.isMustntEndWithDigit() && checkLastDigit(clearPassword)) {
+            if (this.conf.isMustntEndWithDigit() && checkLastDigit(clearPassword)) {
                 throw new PasswordPolicyException("Password mustn't end with a digit");
             }
 
             // check alphanumeric characters occurence
-            if (conf.isAlphanumericRequired() && !checkAlphanumeric(clearPassword)) {
+            if (this.conf.isAlphanumericRequired() && !checkAlphanumeric(clearPassword)) {
                 throw new PasswordPolicyException("Password must contain alphanumeric character(s)");
             }
 
             // check non alphanumeric characters occurence
-            if (conf.isNonAlphanumericRequired() && !checkNonAlphanumeric(clearPassword)) {
+            if (this.conf.isNonAlphanumericRequired() && !checkNonAlphanumeric(clearPassword)) {
                 throw new PasswordPolicyException("Password must contain non-alphanumeric character(s)");
             }
 
             // check alphanumeric character first occurrence
-            if (conf.isMustStartWithAlpha() && !checkFirstAlphanumeric(clearPassword)) {
+            if (this.conf.isMustStartWithAlpha() && !checkFirstAlphanumeric(clearPassword)) {
                 throw new PasswordPolicyException("Password must start with an alphanumeric character");
             }
 
-            if (conf.isMustntStartWithAlpha() && checkFirstAlphanumeric(clearPassword)) {
+            if (this.conf.isMustntStartWithAlpha() && checkFirstAlphanumeric(clearPassword)) {
                 throw new PasswordPolicyException("Password mustn't start with an alphanumeric character");
             }
 
             // check alphanumeric character last occurrence
-            if (conf.isMustEndWithAlpha() && !checkLastAlphanumeric(clearPassword)) {
+            if (this.conf.isMustEndWithAlpha() && !checkLastAlphanumeric(clearPassword)) {
                 throw new PasswordPolicyException("Password must end with an alphanumeric character");
             }
 
-            if (conf.isMustntEndWithAlpha() && checkLastAlphanumeric(clearPassword)) {
+            if (this.conf.isMustntEndWithAlpha() && checkLastAlphanumeric(clearPassword)) {
                 throw new PasswordPolicyException("Password mustn't end with an alphanumeric character");
             }
 
             // check non alphanumeric character first occurrence
-            if (conf.isMustStartWithNonAlpha() && !checkFirstNonAlphanumeric(clearPassword)) {
+            if (this.conf.isMustStartWithNonAlpha() && !checkFirstNonAlphanumeric(clearPassword)) {
                 throw new PasswordPolicyException("Password must start with a non-alphanumeric character");
             }
 
-            if (conf.isMustntStartWithNonAlpha() && checkFirstNonAlphanumeric(clearPassword)) {
+            if (this.conf.isMustntStartWithNonAlpha() && checkFirstNonAlphanumeric(clearPassword)) {
                 throw new PasswordPolicyException("Password mustn't start with a non-alphanumeric character");
             }
 
             // check non alphanumeric character last occurrence
-            if (conf.isMustEndWithNonAlpha() && !checkLastNonAlphanumeric(clearPassword)) {
+            if (this.conf.isMustEndWithNonAlpha() && !checkLastNonAlphanumeric(clearPassword)) {
                 throw new PasswordPolicyException("Password must end with a non-alphanumeric character");
             }
 
-            if (conf.isMustntEndWithNonAlpha() && checkLastNonAlphanumeric(clearPassword)) {
+            if (this.conf.isMustntEndWithNonAlpha() && checkLastNonAlphanumeric(clearPassword)) {
                 throw new PasswordPolicyException("Password mustn't end with a non-alphanumeric character");
             }
+
+            if (!this.conf.isUsernameAllowed()
+                    && user.getUsername() != null && user.getUsername().equals(clearPassword)) {
+
+                throw new PasswordPolicyException("Password mustn't be equal to username");
+            }
         }
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAExternalResourceDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAExternalResourceDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAExternalResourceDAO.java
index 4a8a759..3905d92 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAExternalResourceDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAExternalResourceDAO.java
@@ -32,7 +32,7 @@ import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.TaskDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
-import org.apache.syncope.core.persistence.api.entity.AccountPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
 import org.apache.syncope.core.persistence.api.entity.Policy;

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPolicyDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPolicyDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPolicyDAO.java
index ab0a8c5..4b89f8c 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPolicyDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAPolicyDAO.java
@@ -24,13 +24,13 @@ import javax.persistence.TypedQuery;
 import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
-import org.apache.syncope.core.persistence.api.entity.AccountPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
-import org.apache.syncope.core.persistence.api.entity.PasswordPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.persistence.api.entity.Policy;
 import org.apache.syncope.core.persistence.api.entity.Realm;
-import org.apache.syncope.core.persistence.jpa.entity.JPAPolicy;
-import org.apache.syncope.core.persistence.jpa.entity.JPAAccountPolicy;
+import org.apache.syncope.core.persistence.jpa.entity.policy.AbstractPolicy;
+import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccountPolicy;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Repository;
 
@@ -44,7 +44,7 @@ public class JPAPolicyDAO extends AbstractDAO<Policy, Long> implements PolicyDAO
     @SuppressWarnings("unchecked")
     public <T extends Policy> T find(final Long key) {
         final Query query = entityManager().createQuery(
-                "SELECT e FROM " + JPAPolicy.class.getSimpleName() + " e WHERE e.id=:id");
+                "SELECT e FROM " + AbstractPolicy.class.getSimpleName() + " e WHERE e.id=:id");
         query.setParameter("id", key);
 
         List<T> result = query.getResultList();
@@ -57,7 +57,7 @@ public class JPAPolicyDAO extends AbstractDAO<Policy, Long> implements PolicyDAO
     @SuppressWarnings("unchecked")
     public <T extends Policy> List<T> find(final PolicyType type) {
         final Query query = entityManager().createQuery(
-                "SELECT e FROM " + JPAPolicy.class.getSimpleName() + " e WHERE e.type=:type");
+                "SELECT e FROM " + AbstractPolicy.class.getSimpleName() + " e WHERE e.type=:type");
         query.setParameter("type", type);
 
         return (List<T>) query.getResultList();
@@ -76,7 +76,7 @@ public class JPAPolicyDAO extends AbstractDAO<Policy, Long> implements PolicyDAO
     @Override
     public List<Policy> findAll() {
         TypedQuery<Policy> query = entityManager().createQuery(
-                "SELECT e FROM " + JPAPolicy.class.getSimpleName() + " e", Policy.class);
+                "SELECT e FROM " + AbstractPolicy.class.getSimpleName() + " e", Policy.class);
         return query.getResultList();
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java
index 36ce550..eee37f1 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPARealmDAO.java
@@ -32,7 +32,7 @@ import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.syncope.core.persistence.api.dao.MalformedPathException;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
 import org.apache.syncope.core.persistence.api.dao.RoleDAO;
-import org.apache.syncope.core.persistence.api.entity.AccountPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.Policy;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.Role;

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java
index a70f65e..b4b5342 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPATaskDAO.java
@@ -33,7 +33,7 @@ import org.apache.syncope.core.persistence.jpa.entity.task.JPAPropagationTask;
 import org.apache.syncope.core.persistence.jpa.entity.task.JPAPushTask;
 import org.apache.syncope.core.persistence.jpa.entity.task.JPASchedTask;
 import org.apache.syncope.core.persistence.jpa.entity.task.JPASyncTask;
-import org.apache.syncope.core.persistence.jpa.entity.task.JPATask;
+import org.apache.syncope.core.persistence.jpa.entity.task.AbstractTask;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -75,7 +75,7 @@ public class JPATaskDAO extends AbstractDAO<Task, Long> implements TaskDAO {
     @SuppressWarnings("unchecked")
     @Override
     public <T extends Task> T find(final Long key) {
-        return (T) entityManager().find(JPATask.class, key);
+        return (T) entityManager().find(AbstractTask.class, key);
     }
 
     private <T extends Task> StringBuilder buildfindAllQuery(final TaskType type) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
index 7dd3fe7..130c01e 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
@@ -18,10 +18,13 @@
  */
 package org.apache.syncope.core.persistence.jpa.dao;
 
+import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import javax.annotation.Resource;
 import javax.persistence.NoResultException;
@@ -29,17 +32,16 @@ import javax.persistence.TypedQuery;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Predicate;
 import org.apache.commons.collections4.Transformer;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.ImmutablePair;
 import org.apache.commons.lang3.tuple.Pair;
-import org.apache.syncope.common.lib.types.AccountPolicySpec;
+import org.apache.syncope.common.lib.policy.AccountRuleConf;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.common.lib.types.Entitlement;
 import org.apache.syncope.common.lib.types.EntityViolationType;
-import org.apache.syncope.common.lib.types.PasswordPolicySpec;
-import org.apache.syncope.core.misc.policy.AccountPolicyEnforcer;
+import org.apache.syncope.common.lib.policy.PasswordRuleConf;
 import org.apache.syncope.core.misc.policy.AccountPolicyException;
-import org.apache.syncope.core.misc.policy.PasswordPolicyEnforcer;
-import org.apache.syncope.core.misc.policy.PolicyEvaluator;
+import org.apache.syncope.core.misc.policy.PasswordPolicyException;
 import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
@@ -49,13 +51,17 @@ import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
 import org.apache.syncope.core.misc.security.AuthContextUtils;
 import org.apache.syncope.core.misc.security.UnauthorizedException;
+import org.apache.syncope.core.misc.spring.ApplicationContextProvider;
 import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
+import org.apache.syncope.core.persistence.api.dao.AccountRule;
+import org.apache.syncope.core.persistence.api.dao.AccountRuleConfClass;
+import org.apache.syncope.core.persistence.api.dao.PasswordRule;
+import org.apache.syncope.core.persistence.api.dao.PasswordRuleConfClass;
 import org.apache.syncope.core.persistence.api.dao.RealmDAO;
 import org.apache.syncope.core.persistence.api.dao.RoleDAO;
-import org.apache.syncope.core.persistence.api.entity.AccountPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
-import org.apache.syncope.core.persistence.api.entity.PasswordPolicy;
-import org.apache.syncope.core.persistence.api.entity.Policy;
+import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.Role;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
@@ -64,13 +70,55 @@ import org.apache.syncope.core.persistence.jpa.entity.JPAAnyUtilsFactory;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPADynRoleMembership;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUDynGroupMembership;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
+import org.springframework.core.type.filter.AssignableTypeFilter;
 import org.springframework.stereotype.Repository;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.ClassUtils;
 
 @Repository
 public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
 
+    private static final Map<Class<? extends AccountRuleConf>, Class<AccountRule>> ACCOUNT_RULES_CLASSES =
+            new HashMap<>();
+
+    private static final Map<Class<? extends PasswordRuleConf>, Class<PasswordRule>> PASSWORD_RULES_CLASSES =
+            new HashMap<>();
+
+    static {
+        initRules();
+    }
+
+    @SuppressWarnings("unchecked")
+    private static void initRules() {
+        ClassPathScanningCandidateComponentProvider scanner =
+                new ClassPathScanningCandidateComponentProvider(false);
+        scanner.addIncludeFilter(new AssignableTypeFilter(AccountRule.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(PasswordRule.class));
+
+        for (BeanDefinition bd : scanner.findCandidateComponents(StringUtils.EMPTY)) {
+            Class<?> clazz = ClassUtils.resolveClassName(
+                    bd.getBeanClassName(), ClassUtils.getDefaultClassLoader());
+            boolean isAbstract = Modifier.isAbstract(clazz.getModifiers());
+
+            if (AccountRule.class.isAssignableFrom(clazz) && !isAbstract) {
+                AccountRuleConfClass annotation = clazz.getAnnotation(AccountRuleConfClass.class);
+                if (annotation != null) {
+                    ACCOUNT_RULES_CLASSES.put(annotation.value(), (Class<AccountRule>) clazz);
+                }
+            }
+            if (PasswordRule.class.isAssignableFrom(clazz) && !isAbstract) {
+                PasswordRuleConfClass annotation = clazz.getAnnotation(PasswordRuleConfClass.class);
+                if (annotation != null) {
+                    PASSWORD_RULES_CLASSES.put(annotation.value(), (Class<PasswordRule>) clazz);
+                }
+            }
+        }
+    }
+
     @Autowired
     private RealmDAO realmDAO;
 
@@ -86,15 +134,6 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
     @Resource(name = "anonymousUser")
     private String anonymousUser;
 
-    @Autowired
-    private PolicyEvaluator evaluator;
-
-    @Autowired
-    private PasswordPolicyEnforcer ppEnforcer;
-
-    @Autowired
-    private AccountPolicyEnforcer apEnforcer;
-
     @Override
     protected AnyUtils init() {
         return new JPAAnyUtilsFactory().getInstance(AnyTypeKind.USER);
@@ -235,14 +274,39 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
 
         try {
             int maxPPSpecHistory = 0;
-            for (Policy policy : getPasswordPolicies(user)) {
-                // evaluate policy
-                PasswordPolicySpec ppSpec = evaluator.evaluate(policy, user);
-                // enforce policy
-                ppEnforcer.enforce(ppSpec, user);
-
-                if (ppSpec.getHistoryLength() > maxPPSpecHistory) {
-                    maxPPSpecHistory = ppSpec.getHistoryLength();
+            for (PasswordPolicy policy : getPasswordPolicies(user)) {
+                if (user.getPassword() == null && !policy.isAllowNullPassword()) {
+                    throw new PasswordPolicyException("Password mandatory");
+                }
+
+                for (PasswordRuleConf ruleConf : policy.getRuleConfs()) {
+                    Class<PasswordRule> ruleClass = PASSWORD_RULES_CLASSES.get(ruleConf.getClass());
+                    if (ruleClass == null) {
+                        LOG.warn("Could not find matching password rule for {}", ruleConf.getClass());
+                    } else {
+                        // fetch (or create) rule
+                        PasswordRule rule;
+                        if (ApplicationContextProvider.getBeanFactory().containsSingleton(ruleClass.getName())) {
+                            rule = (PasswordRule) ApplicationContextProvider.getBeanFactory().
+                                    getSingleton(ruleClass.getName());
+                        } else {
+                            rule = (PasswordRule) ApplicationContextProvider.getBeanFactory().
+                                    createBean(ruleClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
+                            ApplicationContextProvider.getBeanFactory().
+                                    registerSingleton(ruleClass.getName(), rule);
+                        }
+
+                        // enforce rule
+                        rule.enforce(ruleConf, user);
+                    }
+                }
+
+                if (user.verifyPasswordHistory(user.getClearPassword(), policy.getHistoryLength())) {
+                    throw new PasswordPolicyException("Password value was used in the past: not allowed");
+                }
+
+                if (policy.getHistoryLength() > maxPPSpecHistory) {
+                    maxPPSpecHistory = policy.getHistoryLength();
                 }
             }
 
@@ -276,14 +340,32 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
                 throw new AccountPolicyException("Not allowed: " + user.getUsername());
             }
 
-            // invalid username
-            for (Policy policy : getAccountPolicies(user)) {
-                // evaluate policy
-                AccountPolicySpec apSpec = evaluator.evaluate(policy, user);
+            for (AccountPolicy policy : getAccountPolicies(user)) {
+                for (AccountRuleConf ruleConf : policy.getRuleConfs()) {
+                    Class<AccountRule> ruleClass = ACCOUNT_RULES_CLASSES.get(ruleConf.getClass());
+                    if (ruleClass == null) {
+                        LOG.warn("Could not find matching password rule for {}", ruleConf.getClass());
+                    } else {
+                        // fetch (or create) rule
+                        AccountRule rule;
+                        if (ApplicationContextProvider.getBeanFactory().containsSingleton(ruleClass.getName())) {
+                            rule = (AccountRule) ApplicationContextProvider.getBeanFactory().
+                                    getSingleton(ruleClass.getName());
+                        } else {
+                            rule = (AccountRule) ApplicationContextProvider.getBeanFactory().
+                                    createBean(ruleClass, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, false);
+                            ApplicationContextProvider.getBeanFactory().
+                                    registerSingleton(ruleClass.getName(), rule);
+                        }
+
+                        // enforce rule
+                        rule.enforce(ruleConf, user);
+                    }
+                }
 
-                // enforce policy
-                suspend |= apEnforcer.enforce(apSpec, user);
-                propagateSuspension |= apSpec.isPropagateSuspension();
+                suspend |= user.getFailedLogins() != null && policy.getMaxAuthenticationAttempts() > 0
+                        && user.getFailedLogins() > policy.getMaxAuthenticationAttempts() && !user.isSuspended();
+                propagateSuspension |= policy.isPropagateSuspension();
             }
         } catch (Exception e) {
             LOG.error("Invalid username for {}", user, e);

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAccountPolicy.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAccountPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAccountPolicy.java
deleted file mode 100644
index 844b49a..0000000
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAAccountPolicy.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.persistence.jpa.entity;
-
-import java.util.HashSet;
-import java.util.Set;
-import javax.persistence.DiscriminatorValue;
-import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.JoinColumn;
-import javax.persistence.JoinTable;
-import javax.persistence.ManyToMany;
-import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.collections4.Transformer;
-import org.apache.syncope.common.lib.types.AccountPolicySpec;
-import org.apache.syncope.common.lib.types.PolicyType;
-import org.apache.syncope.core.misc.serialization.POJOHelper;
-import org.apache.syncope.core.persistence.api.entity.AccountPolicy;
-import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
-import org.apache.syncope.core.persistence.jpa.entity.resource.JPAExternalResource;
-
-@Entity
-@DiscriminatorValue("AccountPolicy")
-public class JPAAccountPolicy extends JPAPolicy implements AccountPolicy {
-
-    private static final long serialVersionUID = -2767606675667839060L;
-
-    /**
-     * Resources for alternative user authentication: if empty, only internal storage will be used.
-     */
-    @ManyToMany(fetch = FetchType.EAGER)
-    @JoinTable(joinColumns =
-            @JoinColumn(name = "accountPolicy_id"),
-            inverseJoinColumns =
-            @JoinColumn(name = "resource_name"))
-    private Set<JPAExternalResource> resources = new HashSet<>();
-
-    public JPAAccountPolicy() {
-        super();
-        this.type = PolicyType.ACCOUNT;
-    }
-
-    @Override
-    public AccountPolicySpec getSpecification() {
-        return POJOHelper.deserialize(specification, AccountPolicySpec.class);
-    }
-
-    @Override
-    public void setSpecification(final AccountPolicySpec policy) {
-        this.specification = POJOHelper.serialize(policy);
-    }
-
-    @Override
-    public boolean add(final ExternalResource resource) {
-        checkType(resource, JPAExternalResource.class);
-        return resources.add((JPAExternalResource) resource);
-    }
-
-    @Override
-    public boolean remove(final ExternalResource resource) {
-        checkType(resource, JPAExternalResource.class);
-        return resources.remove((JPAExternalResource) resource);
-    }
-
-    @Override
-    public Set<? extends ExternalResource> getResources() {
-        return resources;
-    }
-
-    @Override
-    public Set<String> getResourceNames() {
-        return CollectionUtils.collect(getResources(), new Transformer<ExternalResource, String>() {
-
-            @Override
-            public String transform(final ExternalResource input) {
-                return input.getKey();
-            }
-        }, new HashSet<String>());
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
index 41d3f74..586f6cd 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAEntityFactory.java
@@ -18,9 +18,13 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity;
 
+import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPasswordPolicy;
+import org.apache.syncope.core.persistence.jpa.entity.policy.JPASyncPolicy;
+import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccountPolicy;
+import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPushPolicy;
 import org.apache.syncope.core.persistence.jpa.entity.user.JPADynRoleMembership;
 import org.apache.syncope.core.persistence.jpa.entity.resource.JPAExternalResource;
-import org.apache.syncope.core.persistence.api.entity.AccountPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.AnyAbout;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
@@ -34,16 +38,15 @@ import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.Logger;
 import org.apache.syncope.core.persistence.api.entity.Notification;
-import org.apache.syncope.core.persistence.api.entity.PasswordPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.persistence.api.entity.PlainSchema;
-import org.apache.syncope.core.persistence.api.entity.PushPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.api.entity.RelationshipType;
 import org.apache.syncope.core.persistence.api.entity.Report;
 import org.apache.syncope.core.persistence.api.entity.ReportExec;
-import org.apache.syncope.core.persistence.api.entity.ReportletConfInstance;
 import org.apache.syncope.core.persistence.api.entity.Role;
-import org.apache.syncope.core.persistence.api.entity.SyncPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.SyncPolicy;
 import org.apache.syncope.core.persistence.api.entity.VirSchema;
 import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership;
 import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership;
@@ -222,8 +225,6 @@ public class JPAEntityFactory implements EntityFactory {
             result = (T) new JPAReport();
         } else if (reference.equals(ReportExec.class)) {
             result = (T) new JPAReportExec();
-        } else if (reference.equals(ReportletConfInstance.class)) {
-            result = (T) new JPAReportletConfInstance();
         } else if (reference.equals(NotificationTask.class)) {
             result = (T) new JPANotificationTask();
         } else if (reference.equals(PropagationTask.class)) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPasswordPolicy.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPasswordPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPasswordPolicy.java
deleted file mode 100644
index 8a755f4..0000000
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPasswordPolicy.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.persistence.jpa.entity;
-
-import javax.persistence.DiscriminatorValue;
-import javax.persistence.Entity;
-import org.apache.syncope.common.lib.types.PasswordPolicySpec;
-import org.apache.syncope.common.lib.types.PolicyType;
-import org.apache.syncope.core.misc.serialization.POJOHelper;
-import org.apache.syncope.core.persistence.api.entity.PasswordPolicy;
-
-@Entity
-@DiscriminatorValue("PasswordPolicy")
-public class JPAPasswordPolicy extends JPAPolicy implements PasswordPolicy {
-
-    private static final long serialVersionUID = 9138550910385232849L;
-
-    public JPAPasswordPolicy() {
-        super();
-        this.type = PolicyType.PASSWORD;
-    }
-
-    @Override
-    public PasswordPolicySpec getSpecification() {
-        return POJOHelper.deserialize(specification, PasswordPolicySpec.class);
-    }
-
-    @Override
-    public void setSpecification(final PasswordPolicySpec policy) {
-        this.specification = POJOHelper.serialize(policy);
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPolicy.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPolicy.java
deleted file mode 100644
index 1dc5de6..0000000
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPolicy.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.persistence.jpa.entity;
-
-import javax.persistence.DiscriminatorColumn;
-import javax.persistence.Entity;
-import javax.persistence.EnumType;
-import javax.persistence.Enumerated;
-import javax.persistence.Id;
-import javax.persistence.Inheritance;
-import javax.persistence.InheritanceType;
-import javax.persistence.Lob;
-import javax.persistence.Table;
-import javax.validation.constraints.NotNull;
-import org.apache.syncope.common.lib.types.PolicyType;
-import org.apache.syncope.core.persistence.api.entity.Policy;
-
-@Entity
-@Table(name = JPAPolicy.TABLE)
-@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
-@DiscriminatorColumn(name = "DTYPE")
-public abstract class JPAPolicy extends AbstractEntity<Long> implements Policy {
-
-    private static final long serialVersionUID = -5844833125843247458L;
-
-    public static final String TABLE = "Policy";
-
-    @Id
-    private Long id;
-
-    @NotNull
-    private String description;
-
-    @NotNull
-    @Enumerated(EnumType.STRING)
-    protected PolicyType type;
-
-    @Lob
-    protected String specification;
-
-    @Override
-    public Long getKey() {
-        return id;
-    }
-
-    @Override
-    public String getDescription() {
-        return description;
-    }
-
-    @Override
-    public void setDescription(final String description) {
-        this.description = description;
-    }
-
-    @Override
-    public PolicyType getType() {
-        return type;
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPushPolicy.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPushPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPushPolicy.java
deleted file mode 100644
index c3440b2..0000000
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAPushPolicy.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.persistence.jpa.entity;
-
-import javax.persistence.Entity;
-import org.apache.syncope.common.lib.types.PolicyType;
-import org.apache.syncope.common.lib.types.PushPolicySpec;
-import org.apache.syncope.core.misc.serialization.POJOHelper;
-import org.apache.syncope.core.persistence.api.entity.PushPolicy;
-
-@Entity
-public class JPAPushPolicy extends JPAPolicy implements PushPolicy {
-
-    private static final long serialVersionUID = -5875589156893921113L;
-
-    public JPAPushPolicy() {
-        super();
-        this.type = PolicyType.PUSH;
-    }
-
-    @Override
-    public PushPolicySpec getSpecification() {
-        return POJOHelper.deserialize(specification, PushPolicySpec.class);
-    }
-
-    @Override
-    public void setSpecification(final PushPolicySpec policy) {
-        this.specification = POJOHelper.serialize(policy);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java
index a813535..d458178 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPARealm.java
@@ -18,6 +18,8 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity;
 
+import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPasswordPolicy;
+import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccountPolicy;
 import javax.persistence.Cacheable;
 import javax.persistence.Entity;
 import javax.persistence.FetchType;
@@ -28,8 +30,8 @@ import javax.persistence.UniqueConstraint;
 import javax.validation.constraints.Size;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.SyncopeConstants;
-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.AccountPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.persistence.jpa.validation.entity.RealmCheck;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java
index 15c33ea..eeaf86d 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReport.java
@@ -28,12 +28,10 @@ import javax.persistence.Id;
 import javax.persistence.OneToMany;
 import javax.persistence.Table;
 import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.collections4.Predicate;
 import org.apache.commons.collections4.Transformer;
 import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.core.persistence.api.entity.Report;
 import org.apache.syncope.core.persistence.api.entity.ReportExec;
-import org.apache.syncope.core.persistence.api.entity.ReportletConfInstance;
 import org.apache.syncope.core.persistence.jpa.validation.entity.ReportCheck;
 
 @Entity
@@ -52,19 +50,12 @@ public class JPAReport extends AbstractEntity<Long> implements Report {
     private String name;
 
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "report")
-    private List<JPAReportletConfInstance> reportletConfs;
+    private List<JPAReportletConfInstance> reportletConfs = new ArrayList<>();
 
     private String cronExpression;
 
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "report")
-    private List<JPAReportExec> executions;
-
-    public JPAReport() {
-        super();
-
-        reportletConfs = new ArrayList<>();
-        executions = new ArrayList<>();
-    }
+    private List<JPAReportExec> executions = new ArrayList<>();
 
     @Override
     public Long getKey() {
@@ -112,22 +103,16 @@ public class JPAReport extends AbstractEntity<Long> implements Report {
     }
 
     @Override
-    public boolean remove(final ReportletConf reportletConf) {
-        return CollectionUtils.filter(reportletConfs, new Predicate<JPAReportletConfInstance>() {
-
-            @Override
-            public boolean evaluate(final JPAReportletConfInstance object) {
-                return reportletConf.equals(object.getInstance());
-            }
-        });
+    public void removeAllReportletConfs() {
+        reportletConfs.clear();
     }
 
     @Override
     public List<ReportletConf> getReportletConfs() {
-        return CollectionUtils.collect(reportletConfs, new Transformer<ReportletConfInstance, ReportletConf>() {
+        return CollectionUtils.collect(reportletConfs, new Transformer<JPAReportletConfInstance, ReportletConf>() {
 
             @Override
-            public ReportletConf transform(final ReportletConfInstance input) {
+            public ReportletConf transform(final JPAReportletConfInstance input) {
                 return input.getInstance();
             }
         }, new ArrayList<ReportletConf>());

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReportletConfInstance.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReportletConfInstance.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReportletConfInstance.java
index c7edcdc..df08af8 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReportletConfInstance.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPAReportletConfInstance.java
@@ -18,7 +18,6 @@
  */
 package org.apache.syncope.core.persistence.jpa.entity;
 
-import org.apache.syncope.core.persistence.api.entity.ReportletConfInstance;
 import javax.persistence.Entity;
 import javax.persistence.Id;
 import javax.persistence.Lob;
@@ -30,7 +29,7 @@ import org.apache.syncope.core.misc.serialization.POJOHelper;
 
 @Entity
 @Table(name = JPAReportletConfInstance.TABLE)
-public class JPAReportletConfInstance extends AbstractEntity<Long> implements ReportletConfInstance {
+public class JPAReportletConfInstance extends AbstractEntity<Long> {
 
     private static final long serialVersionUID = -2436055132955674610L;
 
@@ -50,25 +49,21 @@ public class JPAReportletConfInstance extends AbstractEntity<Long> implements Re
         return id;
     }
 
-    @Override
     public Report getReport() {
         return report;
     }
 
-    @Override
     public void setReport(final Report report) {
         checkType(report, JPAReport.class);
         this.report = (JPAReport) report;
     }
 
-    @Override
     public ReportletConf getInstance() {
         return serializedInstance == null
                 ? null
                 : POJOHelper.deserialize(serializedInstance, ReportletConf.class);
     }
 
-    @Override
     public void setInstance(final ReportletConf instance) {
         this.serializedInstance = instance == null
                 ? null

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASyncPolicy.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASyncPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASyncPolicy.java
deleted file mode 100644
index e244223..0000000
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/JPASyncPolicy.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.persistence.jpa.entity;
-
-import javax.persistence.DiscriminatorValue;
-import javax.persistence.Entity;
-import org.apache.syncope.common.lib.types.SyncPolicySpec;
-import org.apache.syncope.common.lib.types.PolicyType;
-import org.apache.syncope.core.misc.serialization.POJOHelper;
-import org.apache.syncope.core.persistence.api.entity.SyncPolicy;
-
-@Entity
-@DiscriminatorValue("SyncPolicy")
-public class JPASyncPolicy extends JPAPolicy implements SyncPolicy {
-
-    private static final long serialVersionUID = -6090413855809521279L;
-
-    public JPASyncPolicy() {
-        super();
-        this.type = PolicyType.SYNC;
-    }
-
-    @Override
-    public SyncPolicySpec getSpecification() {
-        return POJOHelper.deserialize(specification, SyncPolicySpec.class);
-    }
-
-    @Override
-    public void setSpecification(final SyncPolicySpec policy) {
-        this.specification = POJOHelper.serialize(policy);
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java
index 87aa04f..e9b38c3 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAAccountPolicy.java
@@ -34,7 +34,6 @@ import javax.persistence.Table;
 import javax.validation.constraints.Max;
 import javax.validation.constraints.Min;
 import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.collections4.Predicate;
 import org.apache.commons.collections4.Transformer;
 import org.apache.syncope.common.lib.policy.AccountRuleConf;
 import org.apache.syncope.common.lib.types.PolicyType;
@@ -58,7 +57,7 @@ public class JPAAccountPolicy extends AbstractPolicy implements AccountPolicy {
     private int maxAuthenticationAttempts;
 
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "accountPolicy")
-    private List<JPAAccountRuleConfInstance> ruleConfs;
+    private List<JPAAccountRuleConfInstance> ruleConfs = new ArrayList<>();
 
     /**
      * Resources for alternative user authentication: if empty, only internal storage will be used.
@@ -109,14 +108,8 @@ public class JPAAccountPolicy extends AbstractPolicy implements AccountPolicy {
     }
 
     @Override
-    public boolean remove(final AccountRuleConf accountRuleConf) {
-        return CollectionUtils.filter(ruleConfs, new Predicate<JPAAccountRuleConfInstance>() {
-
-            @Override
-            public boolean evaluate(final JPAAccountRuleConfInstance object) {
-                return accountRuleConf.equals(object.getInstance());
-            }
-        });
+    public void removeAllRuleConfs() {
+        ruleConfs.clear();
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordPolicy.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordPolicy.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordPolicy.java
index 1491c25..b6013fd 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordPolicy.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/policy/JPAPasswordPolicy.java
@@ -29,7 +29,6 @@ import javax.persistence.Table;
 import javax.validation.constraints.Max;
 import javax.validation.constraints.Min;
 import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.collections4.Predicate;
 import org.apache.commons.collections4.Transformer;
 import org.apache.syncope.common.lib.policy.PasswordRuleConf;
 import org.apache.syncope.common.lib.types.PolicyType;
@@ -51,7 +50,7 @@ public class JPAPasswordPolicy extends AbstractPolicy implements PasswordPolicy
     private int historyLength;
 
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "passwordPolicy")
-    private List<JPAPasswordRuleConfInstance> ruleConfs;
+    private List<JPAPasswordRuleConfInstance> ruleConfs = new ArrayList<>();
 
     public JPAPasswordPolicy() {
         super();
@@ -92,14 +91,8 @@ public class JPAPasswordPolicy extends AbstractPolicy implements PasswordPolicy
     }
 
     @Override
-    public boolean remove(final PasswordRuleConf passwordRuleConf) {
-        return CollectionUtils.filter(ruleConfs, new Predicate<JPAPasswordRuleConfInstance>() {
-
-            @Override
-            public boolean evaluate(final JPAPasswordRuleConfInstance object) {
-                return passwordRuleConf.equals(object.getInstance());
-            }
-        });
+    public void removeAllRuleConfs() {
+        ruleConfs.clear();
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResource.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResource.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResource.java
index b4f7f67..650ef6a 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResource.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/resource/JPAExternalResource.java
@@ -46,20 +46,20 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.types.ConnConfProperty;
 import org.apache.syncope.common.lib.types.PropagationMode;
 import org.apache.syncope.common.lib.types.TraceLevel;
-import org.apache.syncope.core.persistence.api.entity.AccountPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.AccountPolicy;
 import org.apache.syncope.core.persistence.api.entity.ConnInstance;
-import org.apache.syncope.core.persistence.api.entity.PasswordPolicy;
-import org.apache.syncope.core.persistence.api.entity.SyncPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.SyncPolicy;
 import org.apache.syncope.core.persistence.jpa.validation.entity.ExternalResourceCheck;
 import org.apache.syncope.core.misc.serialization.POJOHelper;
 import org.apache.syncope.core.persistence.api.entity.AnyType;
 import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.resource.Provision;
 import org.apache.syncope.core.persistence.jpa.entity.AbstractAnnotatedEntity;
-import org.apache.syncope.core.persistence.jpa.entity.JPAAccountPolicy;
+import org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccountPolicy;
 import org.apache.syncope.core.persistence.jpa.entity.JPAConnInstance;
-import org.apache.syncope.core.persistence.jpa.entity.JPAPasswordPolicy;
-import org.apache.syncope.core.persistence.jpa.entity.JPASyncPolicy;
+import org.apache.syncope.core.persistence.jpa.entity.policy.JPAPasswordPolicy;
+import org.apache.syncope.core.persistence.jpa.entity.policy.JPASyncPolicy;
 import org.identityconnectors.framework.common.objects.ObjectClass;
 
 /**

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractTask.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractTask.java
index 36c52a3..938502d 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractTask.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/AbstractTask.java
@@ -47,9 +47,6 @@ public abstract class AbstractTask extends AbstractEntity<Long> implements Task
 
     public static final String TABLE = "Task";
 
-    /**
-     * Id.
-     */
     @Id
     private Long id;
 
@@ -58,13 +55,7 @@ public abstract class AbstractTask extends AbstractEntity<Long> implements Task
     protected TaskType type;
 
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "task")
-    private List<JPATaskExec> executions;
-
-    public AbstractTask() {
-        super();
-
-        executions = new ArrayList<>();
-    }
+    private List<JPATaskExec> executions = new ArrayList<>();
 
     @Override
     public Long getKey() {

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPANotificationTask.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPANotificationTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPANotificationTask.java
index be72e1e..f483a19 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPANotificationTask.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPANotificationTask.java
@@ -41,7 +41,7 @@ import org.apache.syncope.core.persistence.api.entity.task.NotificationTask;
 
 @Entity
 @DiscriminatorValue("NotificationTask")
-public class JPANotificationTask extends JPATask implements NotificationTask {
+public class JPANotificationTask extends AbstractTask implements NotificationTask {
 
     private static final long serialVersionUID = 95731573485279180L;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPropagationTask.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPropagationTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPropagationTask.java
index cffc4bf..e4d969d 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPropagationTask.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPAPropagationTask.java
@@ -45,7 +45,7 @@ import org.identityconnectors.framework.common.objects.Attribute;
 @Entity
 @DiscriminatorValue("PropagationTask")
 @PropagationTaskCheck
-public class JPAPropagationTask extends JPATask implements PropagationTask {
+public class JPAPropagationTask extends AbstractTask implements PropagationTask {
 
     private static final long serialVersionUID = 7086054884614511210L;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTask.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTask.java
index 4f650ba..d3c0c11 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTask.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPASchedTask.java
@@ -28,7 +28,7 @@ import org.apache.syncope.core.persistence.jpa.validation.entity.SchedTaskCheck;
 @Entity
 @DiscriminatorValue("SchedTask")
 @SchedTaskCheck
-public class JPASchedTask extends JPATask implements SchedTask {
+public class JPASchedTask extends AbstractTask implements SchedTask {
 
     private static final long serialVersionUID = 7596236684832602180L;
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATask.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATask.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATask.java
deleted file mode 100644
index f3c6069..0000000
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATask.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.persistence.jpa.entity.task;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.persistence.CascadeType;
-import javax.persistence.DiscriminatorColumn;
-import javax.persistence.Entity;
-import javax.persistence.EnumType;
-import javax.persistence.Enumerated;
-import javax.persistence.Id;
-import javax.persistence.Inheritance;
-import javax.persistence.InheritanceType;
-import javax.persistence.OneToMany;
-import javax.persistence.Table;
-import javax.validation.constraints.NotNull;
-import org.apache.syncope.common.lib.types.TaskType;
-import org.apache.syncope.core.persistence.api.entity.task.Task;
-import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
-import org.apache.syncope.core.persistence.jpa.entity.AbstractEntity;
-
-@Entity
-@Table(name = JPATask.TABLE)
-@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
-@DiscriminatorColumn(name = "DTYPE")
-public abstract class JPATask extends AbstractEntity<Long> implements Task {
-
-    private static final long serialVersionUID = 5837401178128177511L;
-
-    public static final String TABLE = "Task";
-
-    /**
-     * Id.
-     */
-    @Id
-    private Long id;
-
-    @NotNull
-    @Enumerated(EnumType.STRING)
-    protected TaskType type;
-
-    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "task")
-    private List<JPATaskExec> executions;
-
-    public JPATask() {
-        super();
-
-        executions = new ArrayList<>();
-    }
-
-    @Override
-    public Long getKey() {
-        return id;
-    }
-
-    @Override
-    public TaskType getType() {
-        return type;
-    }
-
-    @Override
-    public boolean addExec(final TaskExec exec) {
-        checkType(exec, JPATaskExec.class);
-        return exec != null && !executions.contains((JPATaskExec) exec) && executions.add((JPATaskExec) exec);
-    }
-
-    @Override
-    public boolean removeExec(final TaskExec exec) {
-        checkType(exec, JPATaskExec.class);
-        return exec != null && executions.remove((JPATaskExec) exec);
-    }
-
-    @Override
-    public List<? extends TaskExec> getExecs() {
-        return executions;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskExec.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskExec.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskExec.java
index 7a37dc8..6ea4fb5 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskExec.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/entity/task/JPATaskExec.java
@@ -29,7 +29,7 @@ import org.apache.syncope.core.persistence.jpa.entity.AbstractExec;
 /**
  * An execution (with result) of a Task.
  *
- * @see JPATask
+ * @see AbstractTask
  */
 @Entity
 @Table(name = JPATaskExec.TABLE)
@@ -39,9 +39,6 @@ public class JPATaskExec extends AbstractExec implements TaskExec {
 
     public static final String TABLE = "TaskExec";
 
-    /**
-     * Id.
-     */
     @Id
     private Long id;
 
@@ -49,7 +46,7 @@ public class JPATaskExec extends AbstractExec implements TaskExec {
      * The referred task.
      */
     @ManyToOne(optional = false)
-    private JPATask task;
+    private AbstractTask task;
 
     @Override
     public Long getKey() {
@@ -63,8 +60,8 @@ public class JPATaskExec extends AbstractExec implements TaskExec {
 
     @Override
     public void setTask(final Task task) {
-        checkType(task, JPATask.class);
-        this.task = (JPATask) task;
+        checkType(task, AbstractTask.class);
+        this.task = (AbstractTask) task;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/resources/META-INF/spring-orm-oracle.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/META-INF/spring-orm-oracle.xml b/core/persistence-jpa/src/main/resources/META-INF/spring-orm-oracle.xml
index a75e4b6..7e08ced 100644
--- a/core/persistence-jpa/src/main/resources/META-INF/spring-orm-oracle.xml
+++ b/core/persistence-jpa/src/main/resources/META-INF/spring-orm-oracle.xml
@@ -380,7 +380,7 @@ under the License.
     </attributes>
   </entity>
 
-  <entity class="org.apache.syncope.core.persistence.jpa.entity.task.JPATask">
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.task.AbstractTask">
     <attributes>
       <id name="id">
         <generated-value generator="SEQ_Task" strategy="TABLE"/>
@@ -397,7 +397,7 @@ under the License.
     </attributes>
   </entity>
     
-  <entity class="org.apache.syncope.core.persistence.jpa.entity.JPAPolicy">
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.policy.AbstractPolicy">
     <attributes>
       <id name="id">
         <generated-value generator="SEQ_Policy" strategy="TABLE"/>
@@ -405,7 +405,23 @@ under the License.
       </id>
     </attributes>
   </entity>
-
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccountRuleConfInstance">
+    <attributes>
+      <id name="id">
+        <generated-value generator="SEQ_AccountRuleConfInstance" strategy="TABLE"/>
+        <table-generator name="SEQ_AccountRuleConfInstance" pk-column-value="SEQ_AccountRuleConfInstance" initial-value="100"/>
+      </id>
+    </attributes>
+  </entity>
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.policy.JPAPasswordRuleConfInstance">
+    <attributes>
+      <id name="id">
+        <generated-value generator="SEQ_PasswordRuleConfInstance" strategy="TABLE"/>
+        <table-generator name="SEQ_PasswordRuleConfInstance" pk-column-value="SEQ_PasswordRuleConfInstance" initial-value="100"/>
+      </id>
+    </attributes>
+  </entity>
+  
   <entity class="org.apache.syncope.core.persistence.jpa.entity.JPAReport">
     <attributes>
       <id name="id">

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/resources/META-INF/spring-orm-sqlserver.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/META-INF/spring-orm-sqlserver.xml b/core/persistence-jpa/src/main/resources/META-INF/spring-orm-sqlserver.xml
index a75e4b6..7e08ced 100644
--- a/core/persistence-jpa/src/main/resources/META-INF/spring-orm-sqlserver.xml
+++ b/core/persistence-jpa/src/main/resources/META-INF/spring-orm-sqlserver.xml
@@ -380,7 +380,7 @@ under the License.
     </attributes>
   </entity>
 
-  <entity class="org.apache.syncope.core.persistence.jpa.entity.task.JPATask">
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.task.AbstractTask">
     <attributes>
       <id name="id">
         <generated-value generator="SEQ_Task" strategy="TABLE"/>
@@ -397,7 +397,7 @@ under the License.
     </attributes>
   </entity>
     
-  <entity class="org.apache.syncope.core.persistence.jpa.entity.JPAPolicy">
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.policy.AbstractPolicy">
     <attributes>
       <id name="id">
         <generated-value generator="SEQ_Policy" strategy="TABLE"/>
@@ -405,7 +405,23 @@ under the License.
       </id>
     </attributes>
   </entity>
-
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccountRuleConfInstance">
+    <attributes>
+      <id name="id">
+        <generated-value generator="SEQ_AccountRuleConfInstance" strategy="TABLE"/>
+        <table-generator name="SEQ_AccountRuleConfInstance" pk-column-value="SEQ_AccountRuleConfInstance" initial-value="100"/>
+      </id>
+    </attributes>
+  </entity>
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.policy.JPAPasswordRuleConfInstance">
+    <attributes>
+      <id name="id">
+        <generated-value generator="SEQ_PasswordRuleConfInstance" strategy="TABLE"/>
+        <table-generator name="SEQ_PasswordRuleConfInstance" pk-column-value="SEQ_PasswordRuleConfInstance" initial-value="100"/>
+      </id>
+    </attributes>
+  </entity>
+  
   <entity class="org.apache.syncope.core.persistence.jpa.entity.JPAReport">
     <attributes>
       <id name="id">

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/persistence-jpa/src/main/resources/META-INF/spring-orm.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/resources/META-INF/spring-orm.xml b/core/persistence-jpa/src/main/resources/META-INF/spring-orm.xml
index 6f83543..7be0d48 100644
--- a/core/persistence-jpa/src/main/resources/META-INF/spring-orm.xml
+++ b/core/persistence-jpa/src/main/resources/META-INF/spring-orm.xml
@@ -428,7 +428,7 @@ under the License.
     </attributes>
   </entity>
 
-  <entity class="org.apache.syncope.core.persistence.jpa.entity.task.JPATask">
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.task.AbstractTask">
     <attributes>
       <id name="id">
         <generated-value generator="SEQ_Task" strategy="TABLE"/>
@@ -445,7 +445,7 @@ under the License.
     </attributes>
   </entity>
     
-  <entity class="org.apache.syncope.core.persistence.jpa.entity.JPAPolicy">
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.policy.AbstractPolicy">
     <attributes>
       <id name="id">
         <generated-value generator="SEQ_Policy" strategy="TABLE"/>
@@ -453,7 +453,23 @@ under the License.
       </id>
     </attributes>
   </entity>
-
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.policy.JPAAccountRuleConfInstance">
+    <attributes>
+      <id name="id">
+        <generated-value generator="SEQ_AccountRuleConfInstance" strategy="TABLE"/>
+        <table-generator name="SEQ_AccountRuleConfInstance" pk-column-value="SEQ_AccountRuleConfInstance" initial-value="100"/>
+      </id>
+    </attributes>
+  </entity>
+  <entity class="org.apache.syncope.core.persistence.jpa.entity.policy.JPAPasswordRuleConfInstance">
+    <attributes>
+      <id name="id">
+        <generated-value generator="SEQ_PasswordRuleConfInstance" strategy="TABLE"/>
+        <table-generator name="SEQ_PasswordRuleConfInstance" pk-column-value="SEQ_PasswordRuleConfInstance" initial-value="100"/>
+      </id>
+    </attributes>
+  </entity>
+    
   <entity class="org.apache.syncope.core.persistence.jpa.entity.JPAReport">
     <attributes>
       <id name="id">


[16/33] syncope git commit: [SYNCOPE-685] Now account and password policies can be composed of several 'rules', where each rule can be provided as a separate Java class (similar to Reportlet mechanism); previous account and password policies are now impl

Posted by md...@apache.org.
[SYNCOPE-685] Now account and password policies can be composed of several 'rules', where each rule can be provided as a separate Java class (similar to Reportlet mechanism); previous account and password policies are now implemented as default rules - also contain SYNCOPE-626


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

Branch: refs/heads/SYNCOPE-156
Commit: b8cadde8ec53da6834792b356cfe40a454193644
Parents: 77f697b
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Thu Aug 20 11:47:26 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Thu Aug 20 11:47:26 2015 +0200

----------------------------------------------------------------------
 .../client/cli/commands/PolicyCommand.java      |   2 +-
 .../client/cli/commands/ReportCommand.java      |  14 -
 .../console/panels/ResourceSecurityPanel.java   |   2 +-
 .../client/console/rest/PolicyRestClient.java   |   2 +-
 .../client/console/rest/ReportRestClient.java   |  18 --
 .../client/lib/SyncopeClientFactoryBean.java    |   2 +-
 .../lib/policy/AbstractAccountRuleConf.java     |  12 +-
 .../lib/policy/AbstractPasswordRuleConf.java    |  12 +-
 .../common/lib/policy/AccountPolicyTO.java      |   4 +-
 .../lib/policy/DefaultAccountRuleConf.java      |  55 +++-
 .../lib/policy/DefaultPasswordRuleConf.java     |  67 +++-
 .../common/lib/policy/PasswordPolicyTO.java     |   4 +-
 .../common/lib/report/StaticReportletConf.java  |   2 +-
 .../syncope/common/lib/to/AbstractPolicyTO.java |  97 ------
 .../syncope/common/lib/to/AccountPolicyTO.java  |  59 ----
 .../syncope/common/lib/to/PasswordPolicyTO.java |  46 ---
 .../syncope/common/lib/to/SyncPolicyTO.java     |  46 ---
 .../apache/syncope/common/lib/to/SyncopeTO.java |  28 ++
 .../common/lib/types/AbstractPolicySpec.java    | 100 ------
 .../common/lib/types/AccountPolicySpec.java     | 121 --------
 .../common/lib/types/PasswordPolicySpec.java    | 300 ------------------
 .../syncope/common/lib/types/PolicyType.java    |  43 ---
 .../common/lib/types/PushPolicySpec.java        |  28 --
 .../common/lib/types/SyncPolicySpec.java        |  62 ----
 .../common/rest/api/service/PolicyService.java  |   2 +-
 .../common/rest/api/service/ReportService.java  |  11 -
 .../apache/syncope/core/logic/PolicyLogic.java  |  14 +-
 .../apache/syncope/core/logic/ReportLogic.java  |  75 -----
 .../apache/syncope/core/logic/SyncopeLogic.java |   7 +
 .../init/ImplementationClassNamesLoader.java    |  13 +
 .../core/logic/report/AbstractReportlet.java    |  21 +-
 .../core/logic/report/GroupReportlet.java       |  13 +-
 .../core/logic/report/ReportJobDelegate.java    |  63 +++-
 .../syncope/core/logic/report/Reportlet.java    |  14 +-
 .../core/logic/report/StaticReportlet.java      |  38 ++-
 .../core/logic/report/UserReportlet.java        |  13 +-
 .../syncope/core/misc/ConnObjectUtils.java      |  24 +-
 .../apache/syncope/core/misc/MappingUtils.java  |   6 +-
 .../core/misc/policy/AccountPolicyEnforcer.java |  85 -----
 .../InvalidPasswordPolicySpecException.java     |  37 ---
 .../misc/policy/PasswordPolicyEnforcer.java     | 193 ------------
 .../core/misc/policy/PolicyEvaluator.java       | 100 ------
 .../misc/security/DefaultPasswordGenerator.java |  96 +++---
 .../core/misc/security/PasswordGenerator.java   | 310 +------------------
 .../misc/src/main/resources/security.properties |   1 +
 .../misc/src/main/resources/securityContext.xml |   1 +
 .../misc/security/PasswordGeneratorTest.java    | 146 ++++-----
 .../core/persistence/api/dao/AccountRule.java   |   9 +-
 .../core/persistence/api/dao/PasswordRule.java  |   9 +-
 .../core/persistence/api/dao/PolicyDAO.java     |   2 +-
 .../persistence/api/entity/AccountPolicy.java   |  38 ---
 .../persistence/api/entity/PasswordPolicy.java  |  29 --
 .../core/persistence/api/entity/PushPolicy.java |  28 --
 .../core/persistence/api/entity/Realm.java      |   3 +
 .../core/persistence/api/entity/Report.java     |   2 +-
 .../api/entity/ReportletConfInstance.java       |  33 --
 .../core/persistence/api/entity/SyncPolicy.java |  29 --
 .../api/entity/policy/AccountPolicy.java        |   2 +-
 .../api/entity/policy/PasswordPolicy.java       |   2 +-
 .../api/entity/resource/ExternalResource.java   |   6 +-
 .../persistence/jpa/dao/DefaultAccountRule.java |  29 +-
 .../jpa/dao/DefaultPasswordRule.java            |  61 ++--
 .../jpa/dao/JPAExternalResourceDAO.java         |   2 +-
 .../core/persistence/jpa/dao/JPAPolicyDAO.java  |  14 +-
 .../core/persistence/jpa/dao/JPARealmDAO.java   |   2 +-
 .../core/persistence/jpa/dao/JPATaskDAO.java    |   4 +-
 .../core/persistence/jpa/dao/JPAUserDAO.java    | 146 +++++++--
 .../jpa/entity/JPAAccountPolicy.java            |  96 ------
 .../jpa/entity/JPAEntityFactory.java            |  15 +-
 .../jpa/entity/JPAPasswordPolicy.java           |  48 ---
 .../core/persistence/jpa/entity/JPAPolicy.java  |  76 -----
 .../persistence/jpa/entity/JPAPushPolicy.java   |  47 ---
 .../core/persistence/jpa/entity/JPARealm.java   |   6 +-
 .../core/persistence/jpa/entity/JPAReport.java  |  27 +-
 .../jpa/entity/JPAReportletConfInstance.java    |   7 +-
 .../persistence/jpa/entity/JPASyncPolicy.java   |  48 ---
 .../jpa/entity/policy/JPAAccountPolicy.java     |  13 +-
 .../jpa/entity/policy/JPAPasswordPolicy.java    |  13 +-
 .../entity/resource/JPAExternalResource.java    |  12 +-
 .../jpa/entity/task/AbstractTask.java           |  11 +-
 .../jpa/entity/task/JPANotificationTask.java    |   2 +-
 .../jpa/entity/task/JPAPropagationTask.java     |   2 +-
 .../jpa/entity/task/JPASchedTask.java           |   2 +-
 .../persistence/jpa/entity/task/JPATask.java    |  96 ------
 .../jpa/entity/task/JPATaskExec.java            |  11 +-
 .../resources/META-INF/spring-orm-oracle.xml    |  22 +-
 .../resources/META-INF/spring-orm-sqlserver.xml |  22 +-
 .../src/main/resources/META-INF/spring-orm.xml  |  22 +-
 .../core/persistence/jpa/inner/PolicyTest.java  |  21 +-
 .../core/persistence/jpa/inner/RealmTest.java   |   4 +-
 .../core/persistence/jpa/inner/UserTest.java    |   6 +-
 .../persistence/jpa/outer/ResourceTest.java     |   2 +-
 .../test/resources/domains/MasterContent.xml    |  50 +--
 .../src/test/resources/persistenceTest.xml      |   2 +
 .../provisioning/api/data/PolicyDataBinder.java |   2 +-
 .../java/data/PolicyDataBinderImpl.java         |  90 ++++--
 .../java/data/RealmDataBinderImpl.java          |   4 +-
 .../java/data/ReportDataBinderImpl.java         |  33 +-
 .../java/data/ResourceDataBinderImpl.java       |   6 +-
 .../provisioning/java/sync/SyncJobDelegate.java |   2 +-
 .../core/provisioning/java/sync/SyncUtils.java  |   2 +-
 .../rest/cxf/service/PolicyServiceImpl.java     |   8 +-
 .../rest/cxf/service/ReportServiceImpl.java     |   7 -
 .../fit/core/reference/TestAccountRuleConf.java |  17 +-
 .../fit/core/reference/TestPasswordRule.java    |  26 +-
 .../core/reference/TestPasswordRuleConf.java    |  19 +-
 .../src/main/resources/log4j2.xml               |   2 +-
 .../fit/core/reference/AbstractITCase.java      |   2 +-
 .../fit/core/reference/PolicyITCase.java        |  51 ++-
 .../syncope/fit/core/reference/RealmITCase.java |  12 +-
 .../fit/core/reference/ReportITCase.java        |   7 +-
 .../fit/core/reference/SyncTaskITCase.java      |   2 +-
 .../syncope/fit/core/reference/UserITCase.java  | 106 +++++++
 113 files changed, 1036 insertions(+), 2904 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/client/cli/src/main/java/org/apache/syncope/client/cli/commands/PolicyCommand.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/PolicyCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/PolicyCommand.java
index 6ea0d33..ae34f53 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/PolicyCommand.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/PolicyCommand.java
@@ -23,7 +23,7 @@ import com.beust.jcommander.Parameters;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.cli.SyncopeServices;
 import org.apache.syncope.common.lib.SyncopeClientException;
-import org.apache.syncope.common.lib.to.AbstractPolicyTO;
+import org.apache.syncope.common.lib.policy.AbstractPolicyTO;
 import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.syncope.common.rest.api.service.PolicyService;
 import org.slf4j.Logger;

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/client/cli/src/main/java/org/apache/syncope/client/cli/commands/ReportCommand.java
----------------------------------------------------------------------
diff --git a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/ReportCommand.java b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/ReportCommand.java
index 2c4dba0..2ea9423 100644
--- a/client/cli/src/main/java/org/apache/syncope/client/cli/commands/ReportCommand.java
+++ b/client/cli/src/main/java/org/apache/syncope/client/cli/commands/ReportCommand.java
@@ -33,7 +33,6 @@ import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.ReportExecTO;
 import org.apache.syncope.common.lib.to.ReportTO;
 import org.apache.syncope.common.lib.types.ReportExecExportFormat;
-import org.apache.syncope.common.lib.wrap.ReportletConfClass;
 import org.apache.syncope.common.rest.api.service.ReportService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -84,9 +83,6 @@ public class ReportCommand extends AbstractCommand {
     @Parameter(names = { "-eer", "--export-execution-result" })
     private Long exportId = -1L;
 
-    @Parameter(names = { "-rc", "--reportlet-class" })
-    private boolean reportletClass = false;
-
     @Override
     public void execute() {
         final ReportService reportService = SyncopeServices.get(ReportService.class);
@@ -176,16 +172,6 @@ public class ReportCommand extends AbstractCommand {
                 LOG.error("Error calling configuration service", ex);
                 System.out.println(" - Error calling configuration service " + ex.getMessage());
             }
-        } else if (reportletClass) {
-            try {
-                LOG.debug("- reportlet configuration class list command");
-                System.out.println("Reportlet conf classes");
-                for (final ReportletConfClass reportletConfClass : reportService.getReportletConfClasses()) {
-                    System.out.println("  *** " + reportletConfClass.getElement());
-                }
-            } catch (final SyncopeClientException ex) {
-                System.out.println(" - Error: " + ex.getMessage());
-            }
         } else {
             System.out.println(helpMessage);
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceSecurityPanel.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceSecurityPanel.java b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceSecurityPanel.java
index 15d2fe8..a0a1ddc 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceSecurityPanel.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/panels/ResourceSecurityPanel.java
@@ -23,7 +23,7 @@ import java.util.HashMap;
 import java.util.Map;
 import org.apache.syncope.client.console.rest.PolicyRestClient;
 import org.apache.syncope.client.console.wicket.markup.html.form.AjaxDropDownChoicePanel;
-import org.apache.syncope.common.lib.to.AbstractPolicyTO;
+import org.apache.syncope.common.lib.policy.AbstractPolicyTO;
 import org.apache.syncope.common.lib.to.ResourceTO;
 import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.wicket.markup.html.WebMarkupContainer;

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/client/console/src/main/java/org/apache/syncope/client/console/rest/PolicyRestClient.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/rest/PolicyRestClient.java b/client/console/src/main/java/org/apache/syncope/client/console/rest/PolicyRestClient.java
index e58f734..7f5964b 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/rest/PolicyRestClient.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/PolicyRestClient.java
@@ -21,7 +21,7 @@ package org.apache.syncope.client.console.rest;
 import java.util.ArrayList;
 import java.util.List;
 import org.apache.syncope.client.console.SyncopeConsoleSession;
-import org.apache.syncope.common.lib.to.AbstractPolicyTO;
+import org.apache.syncope.common.lib.policy.AbstractPolicyTO;
 import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.syncope.common.rest.api.service.PolicyService;
 import org.springframework.stereotype.Component;

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/client/console/src/main/java/org/apache/syncope/client/console/rest/ReportRestClient.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/rest/ReportRestClient.java b/client/console/src/main/java/org/apache/syncope/client/console/rest/ReportRestClient.java
index 05af975..89617df 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/rest/ReportRestClient.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/ReportRestClient.java
@@ -18,14 +18,11 @@
  */
 package org.apache.syncope.client.console.rest;
 
-import java.util.ArrayList;
 import java.util.List;
 import javax.ws.rs.core.Response;
 import org.apache.syncope.client.lib.SyncopeClient;
-import org.apache.syncope.common.lib.SyncopeClientException;
 import org.apache.syncope.common.lib.to.ReportTO;
 import org.apache.syncope.common.lib.types.ReportExecExportFormat;
-import org.apache.syncope.common.lib.wrap.ReportletConfClass;
 import org.apache.syncope.common.rest.api.service.ReportService;
 import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 import org.springframework.stereotype.Component;
@@ -35,21 +32,6 @@ public class ReportRestClient extends BaseRestClient implements ExecutionRestCli
 
     private static final long serialVersionUID = 1644689667998953604L;
 
-    public List<String> getReportletConfClasses() {
-        List<String> result = new ArrayList<>();
-
-        try {
-            List<ReportletConfClass> reportletConfClasses = getService(ReportService.class).getReportletConfClasses();
-            for (ReportletConfClass clazz : reportletConfClasses) {
-                result.add(clazz.getElement());
-            }
-        } catch (SyncopeClientException e) {
-            LOG.error("While getting available reportlet classes", e);
-        }
-
-        return result;
-    }
-
     public ReportTO read(final Long reportId) {
         return getService(ReportService.class).read(reportId);
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java
----------------------------------------------------------------------
diff --git a/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java b/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java
index a3a271c..968260f 100644
--- a/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java
+++ b/client/lib/src/main/java/org/apache/syncope/client/lib/SyncopeClientFactoryBean.java
@@ -31,7 +31,7 @@ import org.apache.cxf.feature.Feature;
 import org.apache.cxf.feature.LoggingFeature;
 import org.apache.cxf.jaxrs.provider.JAXBElementProvider;
 import org.apache.cxf.staxutils.DocumentDepthProperties;
-import org.apache.syncope.common.lib.to.AbstractPolicyTO;
+import org.apache.syncope.common.lib.policy.AbstractPolicyTO;
 import org.apache.syncope.common.rest.api.RESTHeaders;
 
 /**

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractAccountRuleConf.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractAccountRuleConf.java b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractAccountRuleConf.java
index 8fe5e24..8820779 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractAccountRuleConf.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractAccountRuleConf.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.common.lib.report;
+package org.apache.syncope.common.lib.policy;
 
 import javax.xml.bind.annotation.XmlSeeAlso;
 import javax.xml.bind.annotation.XmlType;
@@ -24,19 +24,19 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.AbstractBaseBean;
 
 @XmlType
-@XmlSeeAlso({ StaticReportletConf.class, UserReportletConf.class, GroupReportletConf.class })
-public abstract class AbstractReportletConf extends AbstractBaseBean implements ReportletConf {
+@XmlSeeAlso({ DefaultAccountRuleConf.class })
+public abstract class AbstractAccountRuleConf extends AbstractBaseBean implements AccountRuleConf {
 
-    private static final long serialVersionUID = -6130008602014516608L;
+    private static final long serialVersionUID = -4080475005967851092L;
 
     private String name;
 
-    public AbstractReportletConf() {
+    public AbstractAccountRuleConf() {
         this(StringUtils.EMPTY);
         setName(getClass().getName());
     }
 
-    public AbstractReportletConf(final String name) {
+    public AbstractAccountRuleConf(final String name) {
         super();
         this.name = name;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractPasswordRuleConf.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractPasswordRuleConf.java b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractPasswordRuleConf.java
index 8fe5e24..6d35abd 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractPasswordRuleConf.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AbstractPasswordRuleConf.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.common.lib.report;
+package org.apache.syncope.common.lib.policy;
 
 import javax.xml.bind.annotation.XmlSeeAlso;
 import javax.xml.bind.annotation.XmlType;
@@ -24,19 +24,19 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.AbstractBaseBean;
 
 @XmlType
-@XmlSeeAlso({ StaticReportletConf.class, UserReportletConf.class, GroupReportletConf.class })
-public abstract class AbstractReportletConf extends AbstractBaseBean implements ReportletConf {
+@XmlSeeAlso({ DefaultPasswordRuleConf.class })
+public abstract class AbstractPasswordRuleConf extends AbstractBaseBean implements PasswordRuleConf {
 
-    private static final long serialVersionUID = -6130008602014516608L;
+    private static final long serialVersionUID = -5814018872387142339L;
 
     private String name;
 
-    public AbstractReportletConf() {
+    public AbstractPasswordRuleConf() {
         this(StringUtils.EMPTY);
         setName(getClass().getName());
     }
 
-    public AbstractReportletConf(final String name) {
+    public AbstractPasswordRuleConf(final String name) {
         super();
         this.name = name;
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AccountPolicyTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AccountPolicyTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AccountPolicyTO.java
index 67d9fe0..b1e1049 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AccountPolicyTO.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/AccountPolicyTO.java
@@ -37,7 +37,7 @@ public class AccountPolicyTO extends AbstractPolicyTO {
 
     private int maxAuthenticationAttempts;
 
-    private final List<AccountRuleConf> ruleConfs = new ArrayList<>();
+    private final List<AbstractAccountRuleConf> ruleConfs = new ArrayList<>();
 
     private final List<String> resources = new ArrayList<>();
 
@@ -64,7 +64,7 @@ public class AccountPolicyTO extends AbstractPolicyTO {
     @XmlElementWrapper(name = "ruleConfs")
     @XmlElement(name = "ruleConf")
     @JsonProperty("ruleConfs")
-    public List<AccountRuleConf> getRuleConfs() {
+    public List<AbstractAccountRuleConf> getRuleConfs() {
         return ruleConfs;
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/common/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultAccountRuleConf.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultAccountRuleConf.java b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultAccountRuleConf.java
index 929f05b..804fcc5 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultAccountRuleConf.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultAccountRuleConf.java
@@ -18,12 +18,17 @@
  */
 package org.apache.syncope.common.lib.policy;
 
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
 import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.XmlType;
 
 @XmlRootElement(name = "defaultAccountRuleConf")
 @XmlType
-public class DefaultAccountRuleConf extends AbstractRuleConf implements AccountRuleConf {
+public class DefaultAccountRuleConf extends AbstractAccountRuleConf implements AccountRuleConf {
 
     private static final long serialVersionUID = 3259256974414758406L;
 
@@ -52,6 +57,26 @@ public class DefaultAccountRuleConf extends AbstractRuleConf implements AccountR
      */
     private boolean allLowerCase;
 
+    /**
+     * Substrings not permitted.
+     */
+    private final List<String> wordsNotPermitted = new ArrayList<>();
+
+    /**
+     * User attribute values not permitted.
+     */
+    private final List<String> schemasNotPermitted = new ArrayList<>();
+
+    /**
+     * Substrings not permitted as prefix.
+     */
+    private final List<String> prefixesNotPermitted = new ArrayList<>();
+
+    /**
+     * Substrings not permitted as suffix.
+     */
+    private final List<String> suffixesNotPermitted = new ArrayList<>();
+
     public boolean isAllLowerCase() {
         return allLowerCase;
     }
@@ -91,4 +116,32 @@ public class DefaultAccountRuleConf extends AbstractRuleConf implements AccountR
     public void setPattern(final String pattern) {
         this.pattern = pattern;
     }
+
+    @XmlElementWrapper(name = "wordsNotPermitted")
+    @XmlElement(name = "word")
+    @JsonProperty("wordsNotPermitted")
+    public List<String> getWordsNotPermitted() {
+        return wordsNotPermitted;
+    }
+
+    @XmlElementWrapper(name = "prefixesNotPermitted")
+    @XmlElement(name = "prefix")
+    @JsonProperty("prefixesNotPermitted")
+    public List<String> getPrefixesNotPermitted() {
+        return prefixesNotPermitted;
+    }
+
+    @XmlElementWrapper(name = "schemasNotPermitted")
+    @XmlElement(name = "schema")
+    @JsonProperty("schemasNotPermitted")
+    public List<String> getSchemasNotPermitted() {
+        return schemasNotPermitted;
+    }
+
+    @XmlElementWrapper(name = "suffixesNotPermitted")
+    @XmlElement(name = "suffix")
+    @JsonProperty("suffixesNotPermitted")
+    public List<String> getSuffixesNotPermitted() {
+        return suffixesNotPermitted;
+    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/common/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultPasswordRuleConf.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultPasswordRuleConf.java b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultPasswordRuleConf.java
index 629e820..8792897 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultPasswordRuleConf.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/DefaultPasswordRuleConf.java
@@ -18,12 +18,17 @@
  */
 package org.apache.syncope.common.lib.policy;
 
+import com.fasterxml.jackson.annotation.JsonProperty;
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
 import javax.xml.bind.annotation.XmlRootElement;
 import javax.xml.bind.annotation.XmlType;
 
 @XmlRootElement(name = "defaultPasswordRuleConf")
 @XmlType
-public class DefaultPasswordRuleConf extends AbstractRuleConf implements PasswordRuleConf {
+public class DefaultPasswordRuleConf extends AbstractPasswordRuleConf {
 
     private static final long serialVersionUID = -7988778083915548547L;
 
@@ -122,6 +127,31 @@ public class DefaultPasswordRuleConf extends AbstractRuleConf implements Passwor
      */
     private boolean mustntEndWithAlpha;
 
+    /**
+     * Specify if using username as password is allowed.
+     */
+    private boolean usernameAllowed;
+
+    /**
+     * Substrings not permitted.
+     */
+    private final List<String> wordsNotPermitted = new ArrayList<>();
+
+    /**
+     * User attribute values not permitted.
+     */
+    private final List<String> schemasNotPermitted = new ArrayList<>();
+
+    /**
+     * Substrings not permitted as prefix.
+     */
+    private final List<String> prefixesNotPermitted = new ArrayList<>();
+
+    /**
+     * Substrings not permitted as suffix.
+     */
+    private final List<String> suffixesNotPermitted = new ArrayList<>();
+
     public boolean isDigitRequired() {
         return digitRequired;
     }
@@ -274,4 +304,39 @@ public class DefaultPasswordRuleConf extends AbstractRuleConf implements Passwor
         this.mustntStartWithAlpha = mustntStartWithAlpha;
     }
 
+    public boolean isUsernameAllowed() {
+        return usernameAllowed;
+    }
+
+    public void setUsernameAllowed(final boolean usernameAllowed) {
+        this.usernameAllowed = usernameAllowed;
+    }
+
+    @XmlElementWrapper(name = "wordsNotPermitted")
+    @XmlElement(name = "word")
+    @JsonProperty("wordsNotPermitted")
+    public List<String> getWordsNotPermitted() {
+        return wordsNotPermitted;
+    }
+
+    @XmlElementWrapper(name = "prefixesNotPermitted")
+    @XmlElement(name = "prefix")
+    @JsonProperty("prefixesNotPermitted")
+    public List<String> getPrefixesNotPermitted() {
+        return prefixesNotPermitted;
+    }
+
+    @XmlElementWrapper(name = "schemasNotPermitted")
+    @XmlElement(name = "schema")
+    @JsonProperty("schemasNotPermitted")
+    public List<String> getSchemasNotPermitted() {
+        return schemasNotPermitted;
+    }
+
+    @XmlElementWrapper(name = "suffixesNotPermitted")
+    @XmlElement(name = "suffix")
+    @JsonProperty("suffixesNotPermitted")
+    public List<String> getSuffixesNotPermitted() {
+        return suffixesNotPermitted;
+    }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/common/lib/src/main/java/org/apache/syncope/common/lib/policy/PasswordPolicyTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/policy/PasswordPolicyTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/PasswordPolicyTO.java
index cf91df2..49c73a3 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/policy/PasswordPolicyTO.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/policy/PasswordPolicyTO.java
@@ -37,7 +37,7 @@ public class PasswordPolicyTO extends AbstractPolicyTO {
 
     private int historyLength;
 
-    private final List<PasswordRuleConf> ruleConfs = new ArrayList<>();
+    private final List<AbstractPasswordRuleConf> ruleConfs = new ArrayList<>();
 
     public PasswordPolicyTO() {
         super(PolicyType.PASSWORD);
@@ -62,7 +62,7 @@ public class PasswordPolicyTO extends AbstractPolicyTO {
     @XmlElementWrapper(name = "ruleConfs")
     @XmlElement(name = "ruleConf")
     @JsonProperty("ruleConfs")
-    public List<PasswordRuleConf> getRuleConfs() {
+    public List<AbstractPasswordRuleConf> getRuleConfs() {
         return ruleConfs;
     }
 }

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/common/lib/src/main/java/org/apache/syncope/common/lib/report/StaticReportletConf.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/report/StaticReportletConf.java b/common/lib/src/main/java/org/apache/syncope/common/lib/report/StaticReportletConf.java
index 6d867b2..2514c16 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/report/StaticReportletConf.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/report/StaticReportletConf.java
@@ -44,7 +44,7 @@ public class StaticReportletConf extends AbstractReportletConf {
 
     private TraceLevel traceLevel;
 
-    private final List<String> listField = new ArrayList<String>();
+    private final List<String> listField = new ArrayList<>();
 
     public StaticReportletConf() {
         super();

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/common/lib/src/main/java/org/apache/syncope/common/lib/to/AbstractPolicyTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/AbstractPolicyTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/AbstractPolicyTO.java
deleted file mode 100644
index 2c5fcb4..0000000
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/AbstractPolicyTO.java
+++ /dev/null
@@ -1,97 +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.common.lib.to;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import java.util.ArrayList;
-import java.util.List;
-import javax.ws.rs.PathParam;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlElementWrapper;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlSeeAlso;
-import javax.xml.bind.annotation.XmlType;
-import org.apache.syncope.common.lib.AbstractBaseBean;
-import org.apache.syncope.common.lib.types.PolicyType;
-
-@XmlRootElement(name = "abstractPolicy")
-@XmlType
-@XmlSeeAlso({ AccountPolicyTO.class, PasswordPolicyTO.class, SyncPolicyTO.class })
-@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
-public abstract class AbstractPolicyTO extends AbstractBaseBean {
-
-    private static final long serialVersionUID = -2903888572649721035L;
-
-    private long key;
-
-    private String description;
-
-    private final PolicyType type;
-
-    private final List<String> usedByResources = new ArrayList<>();
-
-    private final List<String> usedByRealms = new ArrayList<>();
-
-    private AbstractPolicyTO() {
-        super();
-        throw new UnsupportedOperationException("No-arg constructor is just to keep JAXB from complaining");
-    }
-
-    protected AbstractPolicyTO(final PolicyType type) {
-        super();
-        this.type = type;
-    }
-
-    public long getKey() {
-        return key;
-    }
-
-    @PathParam("key")
-    public void setKey(final long key) {
-        this.key = key;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public void setDescription(final String description) {
-        this.description = description;
-    }
-
-    public PolicyType getType() {
-        return type;
-    }
-
-    @XmlElementWrapper(name = "usedByResources")
-    @XmlElement(name = "resource")
-    @JsonProperty("usedByResources")
-    public List<String> getUsedByResources() {
-        return usedByResources;
-    }
-
-    @XmlElementWrapper(name = "usedByRealms")
-    @XmlElement(name = "group")
-    @JsonProperty("usedByRealms")
-    public List<String> getUsedByRealms() {
-        return usedByRealms;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/common/lib/src/main/java/org/apache/syncope/common/lib/to/AccountPolicyTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/AccountPolicyTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/AccountPolicyTO.java
deleted file mode 100644
index a701899..0000000
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/AccountPolicyTO.java
+++ /dev/null
@@ -1,59 +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.common.lib.to;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import java.util.ArrayList;
-import java.util.List;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlElementWrapper;
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
-import org.apache.syncope.common.lib.types.AccountPolicySpec;
-import org.apache.syncope.common.lib.types.PolicyType;
-
-@XmlRootElement(name = "accountPolicy")
-@XmlType
-public class AccountPolicyTO extends AbstractPolicyTO {
-
-    private static final long serialVersionUID = -1557150042828800134L;
-
-    private AccountPolicySpec specification;
-
-    private final List<String> resources = new ArrayList<>();
-
-    public AccountPolicyTO() {
-        super(PolicyType.ACCOUNT);
-    }
-
-    public AccountPolicySpec getSpecification() {
-        return specification;
-    }
-
-    public void setSpecification(final AccountPolicySpec specification) {
-        this.specification = specification;
-    }
-
-    @XmlElementWrapper(name = "resources")
-    @XmlElement(name = "resource")
-    @JsonProperty("resources")
-    public List<String> getResources() {
-        return resources;
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/common/lib/src/main/java/org/apache/syncope/common/lib/to/PasswordPolicyTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/PasswordPolicyTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/PasswordPolicyTO.java
deleted file mode 100644
index a0a21ea..0000000
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/PasswordPolicyTO.java
+++ /dev/null
@@ -1,46 +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.common.lib.to;
-
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
-import org.apache.syncope.common.lib.types.PasswordPolicySpec;
-import org.apache.syncope.common.lib.types.PolicyType;
-
-@XmlRootElement(name = "passwordPolicy")
-@XmlType
-public class PasswordPolicyTO extends AbstractPolicyTO {
-
-    private static final long serialVersionUID = -5606086441294799690L;
-
-    private PasswordPolicySpec specification;
-
-    public PasswordPolicyTO() {
-        super(PolicyType.PASSWORD);
-    }
-
-    public PasswordPolicySpec getSpecification() {
-        return specification;
-    }
-
-    public void setSpecification(final PasswordPolicySpec specification) {
-        this.specification = specification;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncPolicyTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncPolicyTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncPolicyTO.java
deleted file mode 100644
index 309a4eb..0000000
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncPolicyTO.java
+++ /dev/null
@@ -1,46 +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.common.lib.to;
-
-import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
-import org.apache.syncope.common.lib.types.PolicyType;
-import org.apache.syncope.common.lib.types.SyncPolicySpec;
-
-@XmlRootElement(name = "syncPolicy")
-@XmlType
-public class SyncPolicyTO extends AbstractPolicyTO {
-
-    private static final long serialVersionUID = 993024634238024242L;
-
-    private SyncPolicySpec specification;
-
-    public SyncPolicyTO() {
-        super(PolicyType.SYNC);
-    }
-
-    public SyncPolicySpec getSpecification() {
-        return specification;
-    }
-
-    public void setSpecification(final SyncPolicySpec specification) {
-        this.specification = specification;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncopeTO.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncopeTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncopeTO.java
index 243a79f..fe57d52 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncopeTO.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/SyncopeTO.java
@@ -59,8 +59,14 @@ public class SyncopeTO extends AbstractBaseBean {
 
     private String virAttrCache;
 
+    private String passwordGenerator;
+
     private final List<String> reportlets = new ArrayList<>();
 
+    private final List<String> accountRules = new ArrayList<>();
+
+    private final List<String> passwordRules = new ArrayList<>();
+
     private final List<String> taskJobs = new ArrayList<>();
 
     private final List<String> propagationActions = new ArrayList<>();
@@ -132,6 +138,14 @@ public class SyncopeTO extends AbstractBaseBean {
         return virAttrCache;
     }
 
+    public String getPasswordGenerator() {
+        return passwordGenerator;
+    }
+
+    public void setPasswordGenerator(final String passwordGenerator) {
+        this.passwordGenerator = passwordGenerator;
+    }
+
     @XmlElementWrapper(name = "reportlets")
     @XmlElement(name = "reportlet")
     @JsonProperty("reportlets")
@@ -139,6 +153,20 @@ public class SyncopeTO extends AbstractBaseBean {
         return reportlets;
     }
 
+    @XmlElementWrapper(name = "accountRules")
+    @XmlElement(name = "accountRule")
+    @JsonProperty("accountRules")
+    public List<String> getAccountRules() {
+        return accountRules;
+    }
+
+    @XmlElementWrapper(name = "passwordRules")
+    @XmlElement(name = "passwordRule")
+    @JsonProperty("passwordRules")
+    public List<String> getPasswordRules() {
+        return passwordRules;
+    }
+
     @XmlElementWrapper(name = "taskJobs")
     @XmlElement(name = "taskJob")
     @JsonProperty("taskJobs")

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/common/lib/src/main/java/org/apache/syncope/common/lib/types/AbstractPolicySpec.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/AbstractPolicySpec.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/AbstractPolicySpec.java
deleted file mode 100644
index e951419..0000000
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/AbstractPolicySpec.java
+++ /dev/null
@@ -1,100 +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.common.lib.types;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import java.util.ArrayList;
-import java.util.List;
-import javax.xml.bind.annotation.XmlElement;
-import javax.xml.bind.annotation.XmlElementWrapper;
-import javax.xml.bind.annotation.XmlType;
-import org.apache.commons.lang3.builder.EqualsBuilder;
-import org.apache.commons.lang3.builder.HashCodeBuilder;
-import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
-import org.apache.commons.lang3.builder.ToStringStyle;
-
-@XmlType
-public abstract class AbstractPolicySpec {
-
-    private static final long serialVersionUID = -6210646284287392063L;
-
-    /**
-     * Substrings not permitted.
-     */
-    private final List<String> wordsNotPermitted = new ArrayList<>();
-
-    /**
-     * User attribute values not permitted.
-     */
-    protected final List<String> schemasNotPermitted = new ArrayList<>();
-
-    /**
-     * Substrings not permitted as prefix.
-     */
-    protected final List<String> prefixesNotPermitted = new ArrayList<>();
-
-    /**
-     * Substrings not permitted as suffix.
-     */
-    protected final List<String> suffixesNotPermitted = new ArrayList<>();
-
-    @XmlElementWrapper(name = "wordsNotPermitted")
-    @XmlElement(name = "word")
-    @JsonProperty("wordsNotPermitted")
-    public List<String> getWordsNotPermitted() {
-        return wordsNotPermitted;
-    }
-
-    @XmlElementWrapper(name = "prefixesNotPermitted")
-    @XmlElement(name = "prefix")
-    @JsonProperty("prefixesNotPermitted")
-    public List<String> getPrefixesNotPermitted() {
-        return prefixesNotPermitted;
-    }
-
-    @XmlElementWrapper(name = "schemasNotPermitted")
-    @XmlElement(name = "schema")
-    @JsonProperty("schemasNotPermitted")
-    public List<String> getSchemasNotPermitted() {
-        return schemasNotPermitted;
-    }
-
-    @XmlElementWrapper(name = "suffixesNotPermitted")
-    @XmlElement(name = "suffix")
-    @JsonProperty("suffixesNotPermitted")
-    public List<String> getSuffixesNotPermitted() {
-        return suffixesNotPermitted;
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        return EqualsBuilder.reflectionEquals(this, obj);
-    }
-
-    @Override
-    public int hashCode() {
-        return HashCodeBuilder.reflectionHashCode(this);
-    }
-
-    @Override
-    public String toString() {
-        return ReflectionToStringBuilder.toString(this, ToStringStyle.MULTI_LINE_STYLE);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/common/lib/src/main/java/org/apache/syncope/common/lib/types/AccountPolicySpec.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/AccountPolicySpec.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/AccountPolicySpec.java
deleted file mode 100644
index 47a60e8..0000000
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/AccountPolicySpec.java
+++ /dev/null
@@ -1,121 +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.common.lib.types;
-
-import javax.xml.bind.annotation.XmlType;
-
-@XmlType
-public class AccountPolicySpec extends AbstractPolicySpec {
-
-    private static final long serialVersionUID = 3259256974414758406L;
-
-    /**
-     * Minimum length.
-     */
-    private int maxLength;
-
-    /**
-     * Maximum length.
-     */
-    private int minLength;
-
-    /**
-     * Pattern (regular expression) that must match.
-     */
-    private String pattern;
-
-    /**
-     * Specify if one or more lowercase characters are permitted.
-     */
-    private boolean allUpperCase;
-
-    /**
-     * Specify if one or more uppercase characters are permitted.
-     */
-    private boolean allLowerCase;
-
-    /**
-     * Specify if, when reached the maximum allowed number of subsequent login failures, user shall be suspended.
-     */
-    private boolean propagateSuspension;
-
-    /**
-     * Maximum number of (failing) authentication attempts.
-     * 0 disabled; &gt;0 enabled.
-     * If the number of subsequent failed logins will be greater then this value
-     * the account will be suspended (lock-out).
-     */
-    private int maxAuthenticationAttempts;
-
-    public boolean isAllLowerCase() {
-        return allLowerCase;
-    }
-
-    public void setAllLowerCase(final boolean allLowerCase) {
-        this.allLowerCase = allLowerCase;
-    }
-
-    public boolean isAllUpperCase() {
-        return allUpperCase;
-    }
-
-    public void setAllUpperCase(final boolean allUpperCase) {
-        this.allUpperCase = allUpperCase;
-    }
-
-    public int getMaxLength() {
-        return maxLength;
-    }
-
-    public void setMaxLength(final int maxLength) {
-        this.maxLength = maxLength;
-    }
-
-    public int getMinLength() {
-        return minLength;
-    }
-
-    public void setMinLength(final int minLength) {
-        this.minLength = minLength;
-    }
-
-    public String getPattern() {
-        return pattern;
-    }
-
-    public void setPattern(final String pattern) {
-        this.pattern = pattern;
-    }
-
-    public boolean isPropagateSuspension() {
-        return propagateSuspension;
-    }
-
-    public void setPropagateSuspension(final boolean propagateSuspension) {
-        this.propagateSuspension = propagateSuspension;
-    }
-
-    public int getMaxAuthenticationAttempts() {
-        return maxAuthenticationAttempts;
-    }
-
-    public void setMaxAuthenticationAttempts(final int maxAuthenticationAttempts) {
-        this.maxAuthenticationAttempts = maxAuthenticationAttempts;
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/common/lib/src/main/java/org/apache/syncope/common/lib/types/PasswordPolicySpec.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/PasswordPolicySpec.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/PasswordPolicySpec.java
deleted file mode 100644
index 175a1ef..0000000
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/PasswordPolicySpec.java
+++ /dev/null
@@ -1,300 +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.common.lib.types;
-
-import javax.xml.bind.annotation.XmlType;
-
-@XmlType
-public class PasswordPolicySpec extends AbstractPolicySpec {
-
-    private static final long serialVersionUID = -7988778083915548547L;
-
-    /**
-     * History length.
-     */
-    private int historyLength;
-
-    /**
-     * Minimum length.
-     */
-    private int maxLength;
-
-    /**
-     * Maximum length.
-     */
-    private int minLength;
-
-    /**
-     * Specify if one or more non alphanumeric characters are required.
-     */
-    private boolean nonAlphanumericRequired;
-
-    /**
-     * Specify if one or more alphanumeric characters are required.
-     */
-    private boolean alphanumericRequired;
-
-    /**
-     * Specify if one or more digits are required.
-     */
-    private boolean digitRequired;
-
-    /**
-     * Specify if one or more lowercase alphabetic characters are required.
-     */
-    private boolean lowercaseRequired;
-
-    /**
-     * Specify if one or more uppercase alphabetic characters are required.
-     */
-    private boolean uppercaseRequired;
-
-    /**
-     * Specify if must start with a digit.
-     */
-    private boolean mustStartWithDigit;
-
-    /**
-     * Specify if mustn't start with a digit.
-     */
-    private boolean mustntStartWithDigit;
-
-    /**
-     * Specify if must end with a digit.
-     */
-    private boolean mustEndWithDigit;
-
-    /**
-     * Specify if mustn't end with a digit.
-     */
-    private boolean mustntEndWithDigit;
-
-    /**
-     * Specify if must start with a non alphanumeric character.
-     */
-    private boolean mustStartWithNonAlpha;
-
-    /**
-     * Specify if must start with a alphanumeric character.
-     */
-    private boolean mustStartWithAlpha;
-
-    /**
-     * Specify if mustn't start with a non alphanumeric character.
-     */
-    private boolean mustntStartWithNonAlpha;
-
-    /**
-     * Specify if mustn't start with a alphanumeric character.
-     */
-    private boolean mustntStartWithAlpha;
-
-    /**
-     * Specify if must end with a non alphanumeric character.
-     */
-    private boolean mustEndWithNonAlpha;
-
-    /**
-     * Specify if must end with a alphanumeric character.
-     */
-    private boolean mustEndWithAlpha;
-
-    /**
-     * Specify if mustn't end with a non alphanumeric character.
-     */
-    private boolean mustntEndWithNonAlpha;
-
-    /**
-     * Specify if mustn't end with a alphanumeric character.
-     */
-    private boolean mustntEndWithAlpha;
-
-    /**
-     * Specify if password shall not be stored internally.
-     */
-    private boolean allowNullPassword;
-
-    public boolean isDigitRequired() {
-        return digitRequired;
-    }
-
-    public void setDigitRequired(final boolean digitRequired) {
-        this.digitRequired = digitRequired;
-    }
-
-    public boolean isLowercaseRequired() {
-        return lowercaseRequired;
-    }
-
-    public void setLowercaseRequired(final boolean lowercaseRequired) {
-        this.lowercaseRequired = lowercaseRequired;
-    }
-
-    public int getMaxLength() {
-        return maxLength;
-    }
-
-    public void setMaxLength(final int maxLength) {
-        this.maxLength = maxLength;
-    }
-
-    public int getMinLength() {
-        return minLength;
-    }
-
-    public void setMinLength(final int minLength) {
-        this.minLength = minLength;
-    }
-
-    public boolean isMustEndWithDigit() {
-        return mustEndWithDigit;
-    }
-
-    public void setMustEndWithDigit(final boolean mustEndWithDigit) {
-        this.mustEndWithDigit = mustEndWithDigit;
-    }
-
-    public boolean isMustEndWithNonAlpha() {
-        return mustEndWithNonAlpha;
-    }
-
-    public void setMustEndWithNonAlpha(final boolean mustEndWithNonAlpha) {
-        this.mustEndWithNonAlpha = mustEndWithNonAlpha;
-    }
-
-    public boolean isMustStartWithDigit() {
-        return mustStartWithDigit;
-    }
-
-    public void setMustStartWithDigit(final boolean mustStartWithDigit) {
-        this.mustStartWithDigit = mustStartWithDigit;
-    }
-
-    public boolean isMustStartWithNonAlpha() {
-        return mustStartWithNonAlpha;
-    }
-
-    public void setMustStartWithNonAlpha(final boolean mustStartWithNonAlpha) {
-        this.mustStartWithNonAlpha = mustStartWithNonAlpha;
-    }
-
-    public boolean isMustntEndWithDigit() {
-        return mustntEndWithDigit;
-    }
-
-    public void setMustntEndWithDigit(final boolean mustntEndWithDigit) {
-        this.mustntEndWithDigit = mustntEndWithDigit;
-    }
-
-    public boolean isMustntEndWithNonAlpha() {
-        return mustntEndWithNonAlpha;
-    }
-
-    public void setMustntEndWithNonAlpha(final boolean mustntEndWithNonAlpha) {
-        this.mustntEndWithNonAlpha = mustntEndWithNonAlpha;
-    }
-
-    public boolean isMustntStartWithDigit() {
-        return mustntStartWithDigit;
-    }
-
-    public void setMustntStartWithDigit(final boolean mustntStartWithDigit) {
-        this.mustntStartWithDigit = mustntStartWithDigit;
-    }
-
-    public boolean isMustntStartWithNonAlpha() {
-        return mustntStartWithNonAlpha;
-    }
-
-    public void setMustntStartWithNonAlpha(final boolean mustntStartWithNonAlpha) {
-        this.mustntStartWithNonAlpha = mustntStartWithNonAlpha;
-    }
-
-    public boolean isNonAlphanumericRequired() {
-        return nonAlphanumericRequired;
-    }
-
-    public void setNonAlphanumericRequired(final boolean nonAlphanumericRequired) {
-        this.nonAlphanumericRequired = nonAlphanumericRequired;
-    }
-
-    public boolean isUppercaseRequired() {
-        return uppercaseRequired;
-    }
-
-    public void setUppercaseRequired(final boolean uppercaseRequired) {
-        this.uppercaseRequired = uppercaseRequired;
-    }
-
-    public boolean isAlphanumericRequired() {
-        return alphanumericRequired;
-    }
-
-    public void setAlphanumericRequired(final boolean alphanumericRequired) {
-        this.alphanumericRequired = alphanumericRequired;
-    }
-
-    public boolean isMustEndWithAlpha() {
-        return mustEndWithAlpha;
-    }
-
-    public void setMustEndWithAlpha(final boolean mustEndWithAlpha) {
-        this.mustEndWithAlpha = mustEndWithAlpha;
-    }
-
-    public boolean isMustStartWithAlpha() {
-        return mustStartWithAlpha;
-    }
-
-    public void setMustStartWithAlpha(final boolean mustStartWithAlpha) {
-        this.mustStartWithAlpha = mustStartWithAlpha;
-    }
-
-    public boolean isMustntEndWithAlpha() {
-        return mustntEndWithAlpha;
-    }
-
-    public void setMustntEndWithAlpha(final boolean mustntEndWithAlpha) {
-        this.mustntEndWithAlpha = mustntEndWithAlpha;
-    }
-
-    public boolean isMustntStartWithAlpha() {
-        return mustntStartWithAlpha;
-    }
-
-    public void setMustntStartWithAlpha(final boolean mustntStartWithAlpha) {
-        this.mustntStartWithAlpha = mustntStartWithAlpha;
-    }
-
-    public int getHistoryLength() {
-        return historyLength;
-    }
-
-    public void setHistoryLength(final int historyLength) {
-        this.historyLength = historyLength;
-    }
-
-    public boolean isAllowNullPassword() {
-        return allowNullPassword;
-    }
-
-    public void setAllowNullPassword(final boolean allowNullPassword) {
-        this.allowNullPassword = allowNullPassword;
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/common/lib/src/main/java/org/apache/syncope/common/lib/types/PolicyType.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/PolicyType.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/PolicyType.java
deleted file mode 100644
index 7f1c11e..0000000
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/PolicyType.java
+++ /dev/null
@@ -1,43 +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.common.lib.types;
-
-import javax.xml.bind.annotation.XmlEnum;
-
-@XmlEnum
-public enum PolicyType {
-
-    /**
-     * How username values should look like.
-     */
-    ACCOUNT,
-    /**
-     * How password values should look like.
-     */
-    PASSWORD,
-    /**
-     * For handling conflicts resolution during synchronization.
-     */
-    SYNC,
-    /**
-     * For handling conflicts resolution during push.
-     */
-    PUSH;
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/common/lib/src/main/java/org/apache/syncope/common/lib/types/PushPolicySpec.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/PushPolicySpec.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/PushPolicySpec.java
deleted file mode 100644
index 0bbea85..0000000
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/PushPolicySpec.java
+++ /dev/null
@@ -1,28 +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.common.lib.types;
-
-import javax.xml.bind.annotation.XmlType;
-
-@XmlType
-public class PushPolicySpec {
-
-    private static final long serialVersionUID = 3641030189482617497L;
-
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/common/lib/src/main/java/org/apache/syncope/common/lib/types/SyncPolicySpec.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/SyncPolicySpec.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/SyncPolicySpec.java
deleted file mode 100644
index 7bdaea0..0000000
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/SyncPolicySpec.java
+++ /dev/null
@@ -1,62 +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.common.lib.types;
-
-import com.fasterxml.jackson.annotation.JsonIgnore;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import java.util.HashMap;
-import java.util.Map;
-import javax.xml.bind.annotation.XmlType;
-import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
-import org.apache.syncope.common.lib.jaxb.XmlGenericMapAdapter;
-
-@XmlType
-public class SyncPolicySpec {
-
-    private static final long serialVersionUID = -3144027171719498127L;
-
-    private ConflictResolutionAction conflictResolutionAction;
-
-    /**
-     * Associates anyTypeKey to either:
-     * <ol>
-     * <li>Java class name, implementing {@code SyncCorrelationRule}</li>
-     * <li>JSON array containing plain schema names - this will be used to feed
-     * {@code PlainAttrsSyncCorrelationRule}</li>
-     * </ol>
-     */
-    @XmlJavaTypeAdapter(XmlGenericMapAdapter.class)
-    @JsonIgnore
-    private final Map<String, String> correlationRules = new HashMap<>();
-
-    public ConflictResolutionAction getConflictResolutionAction() {
-        return conflictResolutionAction == null
-                ? ConflictResolutionAction.IGNORE
-                : conflictResolutionAction;
-    }
-
-    public void setConflictResolutionAction(final ConflictResolutionAction conflictResolutionAction) {
-        this.conflictResolutionAction = conflictResolutionAction;
-    }
-
-    @JsonProperty
-    public Map<String, String> getCorrelationRules() {
-        return correlationRules;
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/PolicyService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/PolicyService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/PolicyService.java
index 980da9f..55b8747 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/PolicyService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/PolicyService.java
@@ -34,7 +34,7 @@ import javax.ws.rs.core.Response;
 import org.apache.cxf.jaxrs.model.wadl.Description;
 import org.apache.cxf.jaxrs.model.wadl.Descriptions;
 import org.apache.cxf.jaxrs.model.wadl.DocTarget;
-import org.apache.syncope.common.lib.to.AbstractPolicyTO;
+import org.apache.syncope.common.lib.policy.AbstractPolicyTO;
 import org.apache.syncope.common.lib.types.PolicyType;
 
 /**

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java
index 17c091d..44863ba 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ReportService.java
@@ -42,7 +42,6 @@ import org.apache.syncope.common.lib.to.ReportTO;
 import org.apache.syncope.common.lib.types.JobAction;
 import org.apache.syncope.common.lib.types.JobStatusType;
 import org.apache.syncope.common.lib.types.ReportExecExportFormat;
-import org.apache.syncope.common.lib.wrap.ReportletConfClass;
 import org.apache.syncope.common.rest.api.beans.ListQuery;
 
 /**
@@ -52,16 +51,6 @@ import org.apache.syncope.common.rest.api.beans.ListQuery;
 public interface ReportService extends JAXRSService {
 
     /**
-     * Returns a list of available classes for reportlet configuration.
-     *
-     * @return list of available classes for reportlet configuration
-     */
-    @GET
-    @Path("reportletConfClasses")
-    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    List<ReportletConfClass> getReportletConfClasses();
-
-    /**
      * Returns report with matching key.
      *
      * @param key key of report to be read

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/logic/src/main/java/org/apache/syncope/core/logic/PolicyLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/PolicyLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/PolicyLogic.java
index 80409aa..e37dc28 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/PolicyLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/PolicyLogic.java
@@ -24,18 +24,18 @@ import java.util.List;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.collections4.Transformer;
 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.policy.AbstractPolicyTO;
+import org.apache.syncope.common.lib.policy.AccountPolicyTO;
+import org.apache.syncope.common.lib.policy.PasswordPolicyTO;
+import org.apache.syncope.common.lib.policy.SyncPolicyTO;
 import org.apache.syncope.common.lib.types.Entitlement;
 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.AccountPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
 import org.apache.syncope.core.persistence.api.entity.Policy;
-import org.apache.syncope.core.persistence.api.entity.SyncPolicy;
+import org.apache.syncope.core.persistence.api.entity.policy.SyncPolicy;
 import org.apache.syncope.core.provisioning.api.data.PolicyDataBinder;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
index d8611ad..3780d30 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/ReportLogic.java
@@ -24,10 +24,8 @@ 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;
@@ -37,13 +35,10 @@ 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.collections4.CollectionUtils;
-import org.apache.commons.collections4.IteratorUtils;
-import org.apache.commons.collections4.PredicateUtils;
 import org.apache.commons.collections4.Transformer;
 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;
@@ -58,10 +53,7 @@ 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.syncope.common.lib.to.AbstractExecTO;
 import org.apache.syncope.common.lib.types.Entitlement;
@@ -74,7 +66,6 @@ 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.util.ClassUtils;
 
 @Component
 public class ReportLogic extends AbstractJobLogic<ReportTO> {
@@ -94,9 +85,6 @@ public class ReportLogic extends AbstractJobLogic<ReportTO> {
     @Autowired
     private EntityFactory entityFactory;
 
-    @Autowired
-    private ImplementationClassNamesLoader classNamesLoader;
-
     @PreAuthorize("hasRole('" + Entitlement.REPORT_CREATE + "')")
     public ReportTO create(final ReportTO reportTO) {
         Report report = entityFactory.newEntity(Report.class);
@@ -156,69 +144,6 @@ public class ReportLogic extends AbstractJobLogic<ReportTO> {
                 }, new ArrayList<ReportTO>());
     }
 
-    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({ "rawtypes" })
-    private Set<Class<Reportlet>> getAllReportletClasses() {
-        return CollectionUtils.collect(IteratorUtils.filteredIterator(
-                classNamesLoader.getClassNames(ImplementationClassNamesLoader.Type.REPORTLET).iterator(),
-                PredicateUtils.notNullPredicate()),
-                new Transformer<String, Class<Reportlet>>() {
-
-                    @SuppressWarnings("unchecked")
-                    @Override
-                    public Class<Reportlet> transform(final String className) {
-                        Class<Reportlet> result = null;
-                        try {
-                            Class reportletClass = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());
-                            result = reportletClass;
-                        } catch (ClassNotFoundException e) {
-                            LOG.warn("Could not load class {}", className);
-                        } catch (LinkageError e) {
-                            LOG.warn("Could not link class {}", className);
-                        }
-
-                        return result;
-                    }
-                }, new HashSet<Class<Reportlet>>());
-    }
-
-    @PreAuthorize("hasRole('" + Entitlement.REPORT_LIST + "')")
-    @SuppressWarnings({ "rawtypes" })
-    public Set<String> getReportletConfClasses() {
-        return CollectionUtils.collect(IteratorUtils.filteredIterator(getAllReportletClasses().iterator(),
-                PredicateUtils.notNullPredicate()),
-                new Transformer<Class<Reportlet>, String>() {
-
-                    @Override
-                    public String transform(final Class<Reportlet> reportletClass) {
-                        Class<? extends ReportletConf> reportletConfClass = getReportletConfClass(reportletClass);
-                        return reportletConfClass == null ? null : reportletConfClass.getName();
-                    }
-                }, new HashSet<String>());
-    }
-
-    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('" + Entitlement.REPORT_READ + "')")
     public ReportTO read(final Long reportKey) {
         Report report = reportDAO.find(reportKey);

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
index d6432e2..eb0c1ad 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java
@@ -28,6 +28,7 @@ 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.security.PasswordGenerator;
 import org.apache.syncope.core.misc.spring.ResourceWithFallbackLoader;
 import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager;
@@ -83,6 +84,9 @@ public class SyncopeLogic extends AbstractLogic<SyncopeTO> {
     private VirAttrCache virAttrCache;
 
     @Autowired
+    private PasswordGenerator passwordGenerator;
+
+    @Autowired
     private ImplementationClassNamesLoader classNamesLoader;
 
     @Resource(name = "velocityResourceLoader")
@@ -129,8 +133,11 @@ public class SyncopeLogic extends AbstractLogic<SyncopeTO> {
         syncopeTO.setUserProvisioningManager(uProvisioningManager.getClass().getName());
         syncopeTO.setGroupProvisioningManager(gProvisioningManager.getClass().getName());
         syncopeTO.setVirAttrCache(virAttrCache.getClass().getName());
+        syncopeTO.setPasswordGenerator(passwordGenerator.getClass().getName());
 
         syncopeTO.getReportlets().addAll(classNamesLoader.getClassNames(Type.REPORTLET));
+        syncopeTO.getAccountRules().addAll(classNamesLoader.getClassNames(Type.ACCOUNT_RULE));
+        syncopeTO.getPasswordRules().addAll(classNamesLoader.getClassNames(Type.PASSWORD_RULE));
         syncopeTO.getTaskJobs().addAll(classNamesLoader.getClassNames(Type.TASKJOBDELEGATE));
         syncopeTO.getPropagationActions().addAll(classNamesLoader.getClassNames(Type.PROPAGATION_ACTIONS));
         syncopeTO.getSyncActions().addAll(classNamesLoader.getClassNames(Type.SYNC_ACTIONS));

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/logic/src/main/java/org/apache/syncope/core/logic/init/ImplementationClassNamesLoader.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/init/ImplementationClassNamesLoader.java b/core/logic/src/main/java/org/apache/syncope/core/logic/init/ImplementationClassNamesLoader.java
index 1b53e5c..7bc842e 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/init/ImplementationClassNamesLoader.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/init/ImplementationClassNamesLoader.java
@@ -32,6 +32,8 @@ import org.apache.syncope.core.provisioning.api.sync.SyncCorrelationRule;
 import org.apache.syncope.core.logic.report.Reportlet;
 import org.apache.syncope.core.persistence.api.SyncopeLoader;
 import org.apache.syncope.core.persistence.api.attrvalue.validation.Validator;
+import org.apache.syncope.core.persistence.api.dao.AccountRule;
+import org.apache.syncope.core.persistence.api.dao.PasswordRule;
 import org.apache.syncope.core.provisioning.api.job.SchedTaskJobDelegate;
 import org.apache.syncope.core.provisioning.java.sync.PushJobDelegate;
 import org.apache.syncope.core.provisioning.java.sync.SyncJobDelegate;
@@ -52,6 +54,8 @@ public class ImplementationClassNamesLoader implements SyncopeLoader {
     public enum Type {
 
         REPORTLET,
+        ACCOUNT_RULE,
+        PASSWORD_RULE,
         TASKJOBDELEGATE,
         PROPAGATION_ACTIONS,
         SYNC_ACTIONS,
@@ -80,6 +84,8 @@ public class ImplementationClassNamesLoader implements SyncopeLoader {
 
         ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
         scanner.addIncludeFilter(new AssignableTypeFilter(Reportlet.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(AccountRule.class));
+        scanner.addIncludeFilter(new AssignableTypeFilter(PasswordRule.class));
         scanner.addIncludeFilter(new AssignableTypeFilter(SchedTaskJobDelegate.class));
         scanner.addIncludeFilter(new AssignableTypeFilter(SyncActions.class));
         scanner.addIncludeFilter(new AssignableTypeFilter(PushActions.class));
@@ -99,6 +105,13 @@ public class ImplementationClassNamesLoader implements SyncopeLoader {
                     classNames.get(Type.REPORTLET).add(clazz.getName());
                 }
 
+                if (AccountRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                    classNames.get(Type.ACCOUNT_RULE).add(clazz.getName());
+                }
+                if (PasswordRule.class.isAssignableFrom(clazz) && !isAbsractClazz) {
+                    classNames.get(Type.PASSWORD_RULE).add(clazz.getName());
+                }
+
                 if (SchedTaskJobDelegate.class.isAssignableFrom(clazz) && !isAbsractClazz
                         && !SyncJobDelegate.class.isAssignableFrom(clazz)
                         && !PushJobDelegate.class.isAssignableFrom(clazz)) {

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java
index 8c1e272..482ad0e 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/AbstractReportlet.java
@@ -18,7 +18,7 @@
  */
 package org.apache.syncope.core.logic.report;
 
-import org.apache.syncope.common.lib.report.AbstractReportletConf;
+import org.apache.syncope.common.lib.report.ReportletConf;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.transaction.annotation.Transactional;
@@ -26,26 +26,15 @@ import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
 
-public abstract class AbstractReportlet<T extends AbstractReportletConf> implements Reportlet<T> {
+public abstract class AbstractReportlet implements Reportlet {
 
     protected static final Logger LOG = LoggerFactory.getLogger(AbstractReportlet.class);
 
-    protected T conf;
-
-    public T getConf() {
-        return conf;
-    }
-
-    @Override
-    public void setConf(final T conf) {
-        this.conf = conf;
-    }
-
-    protected abstract void doExtract(ContentHandler handler) throws SAXException;
+    protected abstract void doExtract(ReportletConf conf, ContentHandler handler) throws SAXException;
 
     @Override
     @Transactional(readOnly = true)
-    public void extract(final ContentHandler handler) throws SAXException {
+    public void extract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
         if (conf == null) {
             throw new ReportException(new IllegalArgumentException("No configuration provided"));
         }
@@ -55,7 +44,7 @@ public abstract class AbstractReportlet<T extends AbstractReportletConf> impleme
         atts.addAttribute("", "", ReportXMLConst.ATTR_CLASS, ReportXMLConst.XSD_STRING, getClass().getName());
         handler.startElement("", "", ReportXMLConst.ELEMENT_REPORTLET, atts);
 
-        doExtract(handler);
+        doExtract(conf, handler);
 
         handler.endElement("", "", ReportXMLConst.ELEMENT_REPORTLET);
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/b8cadde8/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java b/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
index 5b89421..c5e6642 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/report/GroupReportlet.java
@@ -26,6 +26,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.SyncopeConstants;
 import org.apache.syncope.common.lib.report.GroupReportletConf;
 import org.apache.syncope.common.lib.report.GroupReportletConf.Feature;
+import org.apache.syncope.common.lib.report.ReportletConf;
 import org.apache.syncope.common.lib.to.AnyTO;
 import org.apache.syncope.common.lib.to.AttrTO;
 import org.apache.syncope.common.lib.to.GroupTO;
@@ -43,7 +44,7 @@ import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
 
 @ReportletConfClass(GroupReportletConf.class)
-public class GroupReportlet extends AbstractReportlet<GroupReportletConf> {
+public class GroupReportlet extends AbstractReportlet {
 
     private static final int PAGE_SIZE = 10;
 
@@ -56,6 +57,8 @@ public class GroupReportlet extends AbstractReportlet<GroupReportletConf> {
     @Autowired
     private GroupDataBinder groupDataBinder;
 
+    private GroupReportletConf conf;
+
     private List<Group> getPagedGroups(final int page) {
         List<Group> result;
 
@@ -296,7 +299,13 @@ public class GroupReportlet extends AbstractReportlet<GroupReportletConf> {
     }
 
     @Override
-    protected void doExtract(final ContentHandler handler) throws SAXException {
+    protected void doExtract(final ReportletConf conf, final ContentHandler handler) throws SAXException {
+        if (conf instanceof GroupReportletConf) {
+            this.conf = GroupReportletConf.class.cast(conf);
+        } else {
+            throw new ReportException(new IllegalArgumentException("Invalid configuration provided"));
+        }
+
         doExtractConf(handler);
         for (int i = 1; i <= (count() / PAGE_SIZE) + 1; i++) {
             doExtract(handler, getPagedGroups(i));


[29/33] syncope git commit: [SYNCOPE-686] Merge from 1_2_X

Posted by md...@apache.org.
[SYNCOPE-686] Merge from 1_2_X


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

Branch: refs/heads/SYNCOPE-156
Commit: 054ea9ca212911475b6d8b0bd16011c9f831c980
Parents: 3559e69 a1737d3
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Wed Aug 26 16:53:57 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Wed Aug 26 16:53:57 2015 +0200

----------------------------------------------------------------------
 .../console/commons/status/StatusUtils.java     |   2 +-
 .../client/console/rest/GroupRestClient.java    |  15 +--
 .../client/console/rest/UserRestClient.java     |  15 +--
 .../syncope/common/lib/mod/StatusMod.java       |   2 +-
 .../apache/syncope/core/logic/UserLogic.java    |   2 +-
 .../java/DefaultUserProvisioningManager.java    |   4 +-
 .../java/data/UserDataBinderImpl.java           |  40 ++++++
 .../propagation/PropagationManagerImpl.java     |   2 +-
 .../activiti/ActivitiUserWorkflowAdapter.java   |   4 +-
 .../camel/processor/UserProvisionProcessor.java |   2 +-
 .../UserStatusPropagationProcessor.java         |   2 +-
 .../fit/core/reference/SyncTaskITCase.java      |   2 +-
 .../syncope/fit/core/reference/UserITCase.java  | 127 +++++++++++++------
 .../fit/core/reference/UserSelfITCase.java      |   2 +-
 .../fit/core/reference/VirAttrITCase.java       |   4 +-
 15 files changed, 154 insertions(+), 71 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/054ea9ca/client/console/src/main/java/org/apache/syncope/client/console/commons/status/StatusUtils.java
----------------------------------------------------------------------
diff --cc client/console/src/main/java/org/apache/syncope/client/console/commons/status/StatusUtils.java
index 1a6f04f,0000000..4165c7d
mode 100644,000000..100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/commons/status/StatusUtils.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/commons/status/StatusUtils.java
@@@ -1,281 -1,0 +1,281 @@@
 +/*
 + * 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.client.console.commons.status;
 +
 +import java.io.Serializable;
 +import java.util.ArrayList;
 +import java.util.Collection;
 +import java.util.Collections;
 +import java.util.List;
 +import java.util.Map;
 +import org.apache.syncope.client.console.commons.ConnIdSpecialAttributeName;
 +import org.apache.syncope.client.console.commons.Constants;
 +import org.apache.syncope.client.console.panels.ImagePanel;
 +import org.apache.syncope.client.console.rest.AbstractAnyRestClient;
 +import org.apache.syncope.common.lib.mod.StatusMod;
 +import org.apache.syncope.common.lib.to.AnyTO;
 +import org.apache.syncope.common.lib.to.AttrTO;
 +import org.apache.syncope.common.lib.to.ConnObjectTO;
 +import org.apache.wicket.Component;
 +import org.apache.wicket.behavior.Behavior;
 +import org.apache.wicket.markup.ComponentTag;
 +import org.apache.wicket.markup.html.image.Image;
 +import org.apache.wicket.request.resource.ContextRelativeResource;
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +
 +public class StatusUtils implements Serializable {
 +
 +    private static final long serialVersionUID = 7238009174387184309L;
 +
 +    private static final Logger LOG = LoggerFactory.getLogger(StatusUtils.class);
 +
 +    private static final String IMG_PREFIX = "/img/statuses/";
 +
 +    private final AbstractAnyRestClient restClient;
 +
 +    public StatusUtils(final AbstractAnyRestClient restClient) {
 +        this.restClient = restClient;
 +    }
 +
 +    public List<ConnObjectWrapper> getConnectorObjects(final AnyTO any) {
 +        final List<ConnObjectWrapper> objects = new ArrayList<>();
 +        objects.addAll(getConnectorObjects(any, any.getResources()));
 +        return objects;
 +    }
 +
 +    public List<ConnObjectWrapper> getConnectorObjects(
 +            final Collection<AnyTO> anys, final Collection<String> resources) {
 +
 +        final List<ConnObjectWrapper> objects = new ArrayList<>();
 +
 +        for (AnyTO any : anys) {
 +            objects.addAll(getConnectorObjects(any, resources));
 +        }
 +
 +        return objects;
 +    }
 +
 +    private List<ConnObjectWrapper> getConnectorObjects(
 +            final AnyTO any, final Collection<String> resources) {
 +
 +        final List<ConnObjectWrapper> objects = new ArrayList<>();
 +
 +        for (String resourceName : resources) {
 +            ConnObjectTO objectTO = null;
 +            try {
 +                objectTO = restClient.readConnObject(resourceName, any.getKey());
 +            } catch (Exception e) {
 +                LOG.warn("ConnObject '{}' not found on resource '{}'", any.getKey(), resourceName);
 +            }
 +
 +            objects.add(new ConnObjectWrapper(any, resourceName, objectTO));
 +        }
 +
 +        return objects;
 +    }
 +
 +    public StatusBean getStatusBean(
 +            final AnyTO anyTO,
 +            final String resourceName,
 +            final ConnObjectTO objectTO,
 +            final boolean isGroup) {
 +
 +        final StatusBean statusBean = new StatusBean(anyTO, resourceName);
 +
 +        if (objectTO != null) {
 +            final Boolean enabled = isEnabled(objectTO);
 +
 +            final Status status = enabled == null
 +                    ? (isGroup ? Status.ACTIVE : Status.UNDEFINED)
 +                    : enabled
 +                            ? Status.ACTIVE
 +                            : Status.SUSPENDED;
 +
 +            String connObjectLink = getConnObjectLink(objectTO);
 +
 +            statusBean.setStatus(status);
 +            statusBean.setConnObjectLink(connObjectLink);
 +        }
 +
 +        return statusBean;
 +    }
 +
 +    private Boolean isEnabled(final ConnObjectTO objectTO) {
 +        final Map<String, AttrTO> attributeTOs = objectTO.getPlainAttrMap();
 +
 +        final AttrTO status = attributeTOs.get(ConnIdSpecialAttributeName.ENABLE);
 +
 +        return status != null && status.getValues() != null && !status.getValues().isEmpty()
 +                ? Boolean.parseBoolean(status.getValues().get(0))
 +                : null;
 +    }
 +
 +    private String getConnObjectLink(final ConnObjectTO objectTO) {
 +        final Map<String, AttrTO> attributeTOs = objectTO == null
 +                ? Collections.<String, AttrTO>emptyMap()
 +                : objectTO.getPlainAttrMap();
 +
 +        final AttrTO name = attributeTOs.get(ConnIdSpecialAttributeName.NAME);
 +
 +        return name != null && name.getValues() != null && !name.getValues().isEmpty()
 +                ? name.getValues().get(0)
 +                : null;
 +    }
 +
 +    public static StatusMod buildStatusMod(final Collection<StatusBean> statuses) {
 +        return buildStatusMod(statuses, null);
 +    }
 +
 +    public static StatusMod buildStatusMod(final Collection<StatusBean> statuses, final Boolean enable) {
 +        StatusMod statusMod = new StatusMod();
 +        statusMod.setOnSyncope(false);
 +
 +        for (StatusBean status : statuses) {
 +            if (enable == null
 +                    || (enable && !status.getStatus().isActive()) || (!enable && status.getStatus().isActive())) {
 +
 +                if ("syncope".equalsIgnoreCase(status.getResourceName())) {
 +                    statusMod.setOnSyncope(true);
 +                } else {
-                     statusMod.getResourceNames().add(status.getResourceName());
++                    statusMod.getResources().add(status.getResourceName());
 +                }
 +
 +            }
 +        }
 +
 +        return statusMod;
 +    }
 +
 +    public ConnObjectTO getConnObjectTO(
 +            final Long anyKey, final String resourceName, final List<ConnObjectWrapper> objects) {
 +
 +        for (ConnObjectWrapper object : objects) {
 +            if (anyKey.equals(object.getAny().getKey())
 +                    && resourceName.equalsIgnoreCase(object.getResourceName())) {
 +
 +                return object.getConnObjectTO();
 +            }
 +        }
 +
 +        return null;
 +    }
 +
 +    public Image getStatusImage(final String componentId, final Status status) {
 +        final String alt, title, statusName;
 +
 +        switch (status) {
 +
 +            case NOT_YET_SUBMITTED:
 +                statusName = Status.UNDEFINED.toString();
 +                alt = "undefined icon";
 +                title = "Not yet submitted";
 +                break;
 +
 +            case ACTIVE:
 +                statusName = Status.ACTIVE.toString();
 +                alt = "active icon";
 +                title = "Enabled";
 +                break;
 +
 +            case UNDEFINED:
 +                statusName = Status.UNDEFINED.toString();
 +                alt = "undefined icon";
 +                title = "Undefined status";
 +                break;
 +
 +            case OBJECT_NOT_FOUND:
 +                statusName = Status.OBJECT_NOT_FOUND.toString();
 +                alt = "notfound icon";
 +                title = "Not found";
 +                break;
 +
 +            default:
 +                statusName = Status.SUSPENDED.toString();
 +                alt = "inactive icon";
 +                title = "Disabled";
 +        }
 +
 +        final Image img = new Image(componentId,
 +                new ContextRelativeResource(IMG_PREFIX + statusName + Constants.PNG_EXT));
 +        img.add(new Behavior() {
 +
 +            private static final long serialVersionUID = 1469628524240283489L;
 +
 +            @Override
 +            public void onComponentTag(final Component component, final ComponentTag tag) {
 +                tag.put("alt", alt);
 +                tag.put("title", title);
 +            }
 +        });
 +
 +        return img;
 +    }
 +
 +    public ImagePanel getStatusImagePanel(final String componentId, final Status status) {
 +        final String alt, title, statusName;
 +
 +        switch (status) {
 +
 +            case NOT_YET_SUBMITTED:
 +                statusName = Status.UNDEFINED.toString();
 +                alt = "undefined icon";
 +                title = "Not yet submitted";
 +                break;
 +
 +            case ACTIVE:
 +                statusName = Status.ACTIVE.toString();
 +                alt = "active icon";
 +                title = "Enabled";
 +                break;
 +
 +            case UNDEFINED:
 +                statusName = Status.UNDEFINED.toString();
 +                alt = "undefined icon";
 +                title = "Undefined status";
 +                break;
 +
 +            case OBJECT_NOT_FOUND:
 +                statusName = Status.OBJECT_NOT_FOUND.toString();
 +                alt = "notfound icon";
 +                title = "Not found";
 +                break;
 +
 +            default:
 +                statusName = Status.SUSPENDED.toString();
 +                alt = "inactive icon";
 +                title = "Disabled";
 +        }
 +
 +        final ImagePanel imagePanel = new ImagePanel(componentId,
 +                new ContextRelativeResource(IMG_PREFIX + statusName + Constants.PNG_EXT));
 +        imagePanel.add(new Behavior() {
 +
 +            private static final long serialVersionUID = 1469628524240283489L;
 +
 +            @Override
 +            public void onComponentTag(final Component component, final ComponentTag tag) {
 +                tag.put("alt", alt);
 +                tag.put("title", title);
 +            }
 +        });
 +
 +        return imagePanel;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/054ea9ca/client/console/src/main/java/org/apache/syncope/client/console/rest/GroupRestClient.java
----------------------------------------------------------------------
diff --cc client/console/src/main/java/org/apache/syncope/client/console/rest/GroupRestClient.java
index 4644fc0,0000000..ae2c923
mode 100644,000000..100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/rest/GroupRestClient.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/GroupRestClient.java
@@@ -1,204 -1,0 +1,201 @@@
 +/*
 + * 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.client.console.rest;
 +
 +import java.util.List;
 +
 +import javax.ws.rs.core.Response;
 +import org.apache.syncope.client.console.commons.status.StatusBean;
 +import org.apache.syncope.client.console.commons.status.StatusUtils;
 +import org.apache.syncope.client.lib.SyncopeClient;
 +import org.apache.syncope.common.lib.mod.GroupMod;
 +import org.apache.syncope.common.lib.mod.ResourceAssociationMod;
 +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.GroupTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.common.lib.types.ResourceAssociationAction;
 +import org.apache.syncope.common.lib.types.ResourceDeassociationActionType;
 +import org.apache.syncope.common.lib.wrap.ResourceKey;
 +import org.apache.syncope.common.rest.api.CollectionWrapper;
 +import org.apache.syncope.common.rest.api.service.ResourceService;
 +import org.apache.syncope.common.rest.api.service.GroupService;
 +import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 +import org.springframework.stereotype.Component;
 +
 +/**
 + * Console client for invoking Rest Group's services.
 + */
 +@Component
 +public class GroupRestClient extends AbstractAnyRestClient {
 +
 +    private static final long serialVersionUID = -8549081557283519638L;
 +
 +    @Override
 +    public int count(final String realm) {
 +        return getService(GroupService.class).
 +                list(SyncopeClient.getAnyListQueryBuilder().realm(realm).page(1).size(1).build()).
 +                getTotalCount();
 +    }
 +
 +    @Override
 +    public List<GroupTO> list(final String realm, final int page, final int size, final SortParam<String> sort) {
 +        return getService(GroupService.class).
 +                list(SyncopeClient.getAnyListQueryBuilder().realm(realm).page(page).size(size).
 +                        orderBy(toOrderBy(sort)).details(false).build()).
 +                getResult();
 +    }
 +
 +    @Override
 +    public int searchCount(final String realm, final String fiql) {
 +        return getService(GroupService.class).
 +                search(SyncopeClient.getAnySearchQueryBuilder().realm(realm).fiql(fiql).page(1).size(1).build()).
 +                getTotalCount();
 +    }
 +
 +    @Override
 +    public List<GroupTO> search(
 +            final String realm, final String fiql, final int page, final int size, final SortParam<String> sort) {
 +
 +        return getService(GroupService.class).
 +                search(SyncopeClient.getAnySearchQueryBuilder().realm(realm).fiql(fiql).page(page).size(size).
 +                        orderBy(toOrderBy(sort)).details(false).build()).
 +                getResult();
 +    }
 +
 +    @Override
 +    public ConnObjectTO readConnObject(final String resourceName, final Long id) {
 +        return getService(ResourceService.class).readConnObject(resourceName, AnyTypeKind.GROUP.name(), id);
 +    }
 +
 +    public GroupTO create(final GroupTO groupTO) {
 +        Response response = getService(GroupService.class).create(groupTO);
 +        return response.readEntity(GroupTO.class);
 +    }
 +
 +    public GroupTO read(final Long key) {
 +        return getService(GroupService.class).read(key);
 +    }
 +
 +    public GroupTO update(final String etag, final GroupMod groupMod) {
 +        GroupTO result;
 +        synchronized (this) {
 +            GroupService service = getService(etag, GroupService.class);
 +            result = service.update(groupMod).readEntity(GroupTO.class);
 +            resetClient(GroupService.class);
 +        }
 +        return result;
 +    }
 +
 +    @Override
 +    public GroupTO delete(final String etag, final Long key) {
 +        GroupTO result;
 +        synchronized (this) {
 +            GroupService service = getService(etag, GroupService.class);
 +            result = service.delete(key).readEntity(GroupTO.class);
 +            resetClient(GroupService.class);
 +        }
 +        return result;
 +    }
 +
 +    @Override
 +    public BulkActionResult bulkAction(final BulkAction action) {
 +        return getService(GroupService.class).bulk(action);
 +    }
 +
 +    public void unlink(final String etag, final long groupKey, final List<StatusBean> statuses) {
 +        synchronized (this) {
 +            GroupService service = getService(etag, GroupService.class);
 +            service.deassociate(groupKey, ResourceDeassociationActionType.UNLINK,
-                     CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(),
-                             ResourceKey.class));
++                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResources(), ResourceKey.class));
 +            resetClient(GroupService.class);
 +        }
 +    }
 +
 +    public void link(final String etag, final long groupKey, final List<StatusBean> statuses) {
 +        synchronized (this) {
 +            GroupService service = getService(etag, GroupService.class);
 +
 +            ResourceAssociationMod associationMod = new ResourceAssociationMod();
 +            associationMod.getTargetResources().addAll(
-                     CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(), ResourceKey.class));
++                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResources(), ResourceKey.class));
 +            service.associate(groupKey, ResourceAssociationAction.LINK, associationMod);
 +
 +            resetClient(GroupService.class);
 +        }
 +    }
 +
 +    public BulkActionResult deprovision(final String etag, final long groupKey, final List<StatusBean> statuses) {
 +        BulkActionResult result;
 +        synchronized (this) {
 +            GroupService service = getService(etag, GroupService.class);
 +            result = service.deassociate(groupKey, ResourceDeassociationActionType.DEPROVISION,
-                     CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(),
-                             ResourceKey.class)).
++                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResources(), ResourceKey.class)).
 +                    readEntity(BulkActionResult.class);
 +            resetClient(GroupService.class);
 +        }
 +        return result;
 +    }
 +
 +    public BulkActionResult provision(final String etag, final long groupKey, final List<StatusBean> statuses) {
 +        BulkActionResult result;
 +        synchronized (this) {
 +            GroupService service = getService(etag, GroupService.class);
 +
 +            ResourceAssociationMod associationMod = new ResourceAssociationMod();
 +            associationMod.getTargetResources().addAll(
-                     CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(), ResourceKey.class));
++                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResources(), ResourceKey.class));
 +
 +            result = service.associate(groupKey, ResourceAssociationAction.PROVISION, associationMod).
 +                    readEntity(BulkActionResult.class);
 +            resetClient(GroupService.class);
 +        }
 +        return result;
 +    }
 +
 +    public BulkActionResult unassign(final String etag, final long groupKey, final List<StatusBean> statuses) {
 +        BulkActionResult result;
 +        synchronized (this) {
 +            GroupService service = getService(etag, GroupService.class);
 +            result = service.deassociate(groupKey, ResourceDeassociationActionType.UNASSIGN,
-                     CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(),
-                             ResourceKey.class)).
++                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResources(), ResourceKey.class)).
 +                    readEntity(BulkActionResult.class);
 +            resetClient(GroupService.class);
 +        }
 +        return result;
 +    }
 +
 +    public BulkActionResult assign(final String etag, final long groupKey, final List<StatusBean> statuses) {
 +        BulkActionResult result;
 +        synchronized (this) {
 +            GroupService service = getService(etag, GroupService.class);
 +
 +            ResourceAssociationMod associationMod = new ResourceAssociationMod();
 +            associationMod.getTargetResources().addAll(
-                     CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(), ResourceKey.class));
++                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResources(), ResourceKey.class));
 +
 +            result = service.associate(groupKey, ResourceAssociationAction.ASSIGN, associationMod).
 +                    readEntity(BulkActionResult.class);
 +
 +            resetClient(GroupService.class);
 +        }
 +        return result;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/054ea9ca/client/console/src/main/java/org/apache/syncope/client/console/rest/UserRestClient.java
----------------------------------------------------------------------
diff --cc client/console/src/main/java/org/apache/syncope/client/console/rest/UserRestClient.java
index d161658,0000000..36f1124
mode 100644,000000..100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/rest/UserRestClient.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/UserRestClient.java
@@@ -1,240 -1,0 +1,237 @@@
 +/*
 + * 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.client.console.rest;
 +
 +import java.util.List;
 +import javax.ws.rs.core.Response;
 +import org.apache.syncope.client.console.commons.status.StatusBean;
 +import org.apache.syncope.client.console.commons.status.StatusUtils;
 +import org.apache.syncope.client.lib.SyncopeClient;
 +import org.apache.syncope.common.lib.SyncopeClientException;
 +import org.apache.syncope.common.lib.mod.ResourceAssociationMod;
 +import org.apache.syncope.common.lib.mod.StatusMod;
 +import org.apache.syncope.common.lib.mod.UserMod;
 +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.UserTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.common.lib.types.ResourceAssociationAction;
 +import org.apache.syncope.common.lib.types.ResourceDeassociationActionType;
 +import org.apache.syncope.common.lib.wrap.ResourceKey;
 +import org.apache.syncope.common.rest.api.CollectionWrapper;
 +import org.apache.syncope.common.rest.api.service.ResourceService;
 +import org.apache.syncope.common.rest.api.service.UserService;
 +import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
 +import org.springframework.stereotype.Component;
 +
 +/**
 + * Console client for invoking rest users services.
 + */
 +@Component
 +public class UserRestClient extends AbstractAnyRestClient {
 +
 +    private static final long serialVersionUID = -1575748964398293968L;
 +
 +    @Override
 +    public int count(final String realm) {
 +        return getService(UserService.class).
 +                list(SyncopeClient.getAnyListQueryBuilder().realm(realm).page(1).size(1).build()).
 +                getTotalCount();
 +    }
 +
 +    @Override
 +    public List<UserTO> list(final String realm, final int page, final int size, final SortParam<String> sort) {
 +        return getService(UserService.class).
 +                list(SyncopeClient.getAnyListQueryBuilder().realm(realm).page(page).size(size).
 +                        orderBy(toOrderBy(sort)).details(false).build()).
 +                getResult();
 +    }
 +
 +    public UserTO create(final UserTO userTO, final boolean storePassword) {
 +        Response response = getService(UserService.class).create(userTO, storePassword);
 +        return response.readEntity(UserTO.class);
 +    }
 +
 +    public UserTO update(final String etag, final UserMod userMod) {
 +        UserTO result;
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +            result = service.update(userMod).readEntity(UserTO.class);
 +            resetClient(UserService.class);
 +        }
 +        return result;
 +    }
 +
 +    @Override
 +    public UserTO delete(final String etag, final Long id) {
 +        UserTO result;
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +            result = service.delete(id).readEntity(UserTO.class);
 +            resetClient(UserService.class);
 +        }
 +        return result;
 +    }
 +
 +    public UserTO read(final Long id) {
 +        UserTO userTO = null;
 +        try {
 +            userTO = getService(UserService.class).read(id);
 +        } catch (SyncopeClientException e) {
 +            LOG.error("While reading a user", e);
 +        }
 +        return userTO;
 +    }
 +
 +    @Override
 +    public int searchCount(final String realm, final String fiql) {
 +        return getService(UserService.class).
 +                search(SyncopeClient.getAnySearchQueryBuilder().realm(realm).fiql(fiql).page(1).size(1).build()).
 +                getTotalCount();
 +    }
 +
 +    @Override
 +    public List<UserTO> search(
 +            final String realm, final String fiql, final int page, final int size, final SortParam<String> sort) {
 +
 +        return getService(UserService.class).
 +                search(SyncopeClient.getAnySearchQueryBuilder().realm(realm).fiql(fiql).page(page).size(size).
 +                        orderBy(toOrderBy(sort)).details(false).build()).
 +                getResult();
 +    }
 +
 +    @Override
 +    public ConnObjectTO readConnObject(final String resourceName, final Long id) {
 +        return getService(ResourceService.class).readConnObject(resourceName, AnyTypeKind.USER.name(), id);
 +    }
 +
 +    public void suspend(final String etag, final long userKey, final List<StatusBean> statuses) {
 +        StatusMod statusMod = StatusUtils.buildStatusMod(statuses, false);
 +        statusMod.setKey(userKey);
 +        statusMod.setType(StatusMod.ModType.SUSPEND);
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +            service.status(statusMod);
 +            resetClient(UserService.class);
 +        }
 +    }
 +
 +    public void reactivate(final String etag, final long userKey, final List<StatusBean> statuses) {
 +        StatusMod statusMod = StatusUtils.buildStatusMod(statuses, true);
 +        statusMod.setKey(userKey);
 +        statusMod.setType(StatusMod.ModType.REACTIVATE);
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +            service.status(statusMod);
 +            resetClient(UserService.class);
 +        }
 +    }
 +
 +    @Override
 +    public BulkActionResult bulkAction(final BulkAction action) {
 +        return getService(UserService.class).bulk(action);
 +    }
 +
 +    public void unlink(final String etag, final long userKey, final List<StatusBean> statuses) {
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +            service.deassociate(userKey, ResourceDeassociationActionType.UNLINK,
-                     CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(),
-                             ResourceKey.class));
++                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResources(), ResourceKey.class));
 +            resetClient(UserService.class);
 +        }
 +    }
 +
 +    public void link(final String etag, final long userKey, final List<StatusBean> statuses) {
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +
 +            ResourceAssociationMod associationMod = new ResourceAssociationMod();
 +            associationMod.getTargetResources().addAll(
-                     CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(), ResourceKey.class));
++                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResources(), ResourceKey.class));
 +            service.associate(userKey, ResourceAssociationAction.LINK, associationMod);
 +
 +            resetClient(UserService.class);
 +        }
 +    }
 +
 +    public BulkActionResult deprovision(final String etag, final long userKey, final List<StatusBean> statuses) {
 +        BulkActionResult result;
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +            result = service.deassociate(userKey, ResourceDeassociationActionType.DEPROVISION,
-                     CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(),
-                             ResourceKey.class)).
++                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResources(), ResourceKey.class)).
 +                    readEntity(BulkActionResult.class);
 +            resetClient(UserService.class);
 +        }
 +        return result;
 +    }
 +
 +    public BulkActionResult provision(final String etag, final long userKey,
 +            final List<StatusBean> statuses, final boolean changepwd, final String password) {
 +
 +        BulkActionResult result;
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +
 +            ResourceAssociationMod associationMod = new ResourceAssociationMod();
 +            associationMod.getTargetResources().addAll(
-                     CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(), ResourceKey.class));
++                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResources(), ResourceKey.class));
 +            associationMod.setChangePwd(changepwd);
 +            associationMod.setPassword(password);
 +
 +            result = service.associate(userKey, ResourceAssociationAction.PROVISION, associationMod).
 +                    readEntity(BulkActionResult.class);
 +            resetClient(UserService.class);
 +        }
 +        return result;
 +    }
 +
 +    public BulkActionResult unassign(final String etag, final long userKey, final List<StatusBean> statuses) {
 +        BulkActionResult result;
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +            result = service.deassociate(userKey, ResourceDeassociationActionType.UNASSIGN,
-                     CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(),
-                             ResourceKey.class)).
++                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResources(), ResourceKey.class)).
 +                    readEntity(BulkActionResult.class);
 +            resetClient(UserService.class);
 +        }
 +        return result;
 +    }
 +
 +    public BulkActionResult assign(final String etag, final long userKey,
 +            final List<StatusBean> statuses, final boolean changepwd, final String password) {
 +
 +        BulkActionResult result;
 +        synchronized (this) {
 +            UserService service = getService(etag, UserService.class);
 +
 +            ResourceAssociationMod associationMod = new ResourceAssociationMod();
 +            associationMod.getTargetResources().addAll(
-                     CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResourceNames(), ResourceKey.class));
++                    CollectionWrapper.wrap(StatusUtils.buildStatusMod(statuses).getResources(), ResourceKey.class));
 +            associationMod.setChangePwd(changepwd);
 +            associationMod.setPassword(password);
 +
 +            result = service.associate(userKey, ResourceAssociationAction.ASSIGN, associationMod).
 +                    readEntity(BulkActionResult.class);
 +            resetClient(UserService.class);
 +        }
 +        return result;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/054ea9ca/common/lib/src/main/java/org/apache/syncope/common/lib/mod/StatusMod.java
----------------------------------------------------------------------
diff --cc common/lib/src/main/java/org/apache/syncope/common/lib/mod/StatusMod.java
index e3fc901,0000000..7f087b7
mode 100644,000000..100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/mod/StatusMod.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/mod/StatusMod.java
@@@ -1,110 -1,0 +1,110 @@@
 +/*
 + * 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.common.lib.mod;
 +
 +import com.fasterxml.jackson.annotation.JsonProperty;
 +import java.util.ArrayList;
 +import java.util.List;
 +import javax.ws.rs.PathParam;
 +import javax.xml.bind.annotation.XmlElement;
 +import javax.xml.bind.annotation.XmlElementWrapper;
 +import javax.xml.bind.annotation.XmlEnum;
 +import javax.xml.bind.annotation.XmlRootElement;
 +import javax.xml.bind.annotation.XmlType;
 +import org.apache.syncope.common.lib.AbstractBaseBean;
 +
 +@XmlRootElement(name = "statusMod")
 +@XmlType
 +public class StatusMod extends AbstractBaseBean {
 +
 +    private static final long serialVersionUID = 3230910033784302656L;
 +
 +    @XmlEnum
 +    @XmlType(name = "statusModType")
 +    public enum ModType {
 +
 +        ACTIVATE,
 +        SUSPEND,
 +        REACTIVATE;
 +
 +    }
 +
 +    /**
 +     * Key of user to for which status update is requested.
 +     */
 +    private long key;
 +
 +    private ModType type;
 +
 +    /**
 +     * Update token (if required).
 +     */
 +    private String token;
 +
 +    /**
 +     * Whether update should be performed on internal storage.
 +     */
 +    private boolean onSyncope = true;
 +
 +    /**
 +     * External resources for which update is needed to be propagated.
 +     */
 +    private final List<String> resourceNames = new ArrayList<>();
 +
 +    public long getKey() {
 +        return key;
 +    }
 +
 +    @PathParam("key")
 +    public void setKey(final long key) {
 +        this.key = key;
 +    }
 +
 +    public ModType getType() {
 +        return type;
 +    }
 +
 +    public void setType(final ModType type) {
 +        this.type = type;
 +    }
 +
 +    public String getToken() {
 +        return token;
 +    }
 +
 +    public void setToken(final String token) {
 +        this.token = token;
 +    }
 +
 +    public boolean isOnSyncope() {
 +        return onSyncope;
 +    }
 +
 +    public void setOnSyncope(final boolean onSyncope) {
 +        this.onSyncope = onSyncope;
 +    }
 +
 +    @XmlElementWrapper(name = "resources")
 +    @XmlElement(name = "resource")
 +    @JsonProperty("resources")
-     public List<String> getResourceNames() {
++    public List<String> getResources() {
 +        return resourceNames;
 +    }
 +
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/054ea9ca/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
----------------------------------------------------------------------
diff --cc core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
index ff87d6b,0000000..c8a986d
mode 100644,000000..100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
@@@ -1,525 -1,0 +1,525 @@@
 +/*
 + * 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.security.AccessControlException;
 +import java.util.ArrayList;
 +import java.util.Collection;
 +import java.util.Collections;
 +import java.util.HashSet;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Set;
 +import org.apache.commons.collections4.CollectionUtils;
 +import org.apache.commons.collections4.Transformer;
 +import org.apache.commons.lang3.ArrayUtils;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.commons.lang3.tuple.ImmutablePair;
 +import org.apache.commons.lang3.tuple.Pair;
 +import org.apache.syncope.common.lib.SyncopeClientException;
 +import org.apache.syncope.common.lib.mod.StatusMod;
 +import org.apache.syncope.common.lib.mod.UserMod;
 +import org.apache.syncope.common.lib.to.PropagationStatus;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.common.lib.types.ClientExceptionType;
 +import org.apache.syncope.common.lib.types.Entitlement;
 +import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 +import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 +import org.apache.syncope.core.persistence.api.dao.UserDAO;
 +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.group.Group;
 +import org.apache.syncope.core.persistence.api.entity.user.User;
 +import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
 +import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
 +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.AuthContextUtils;
 +import org.apache.syncope.core.misc.serialization.POJOHelper;
 +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
 +import org.apache.syncope.core.provisioning.api.AnyTransformer;
 +import org.apache.syncope.core.provisioning.api.VirAttrHandler;
 +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;
 +
 +/**
 + * Note that this controller does not extend {@link AbstractTransactionalLogic}, hence does not provide any
 + * Spring's Transactional logic at class level.
 + */
 +@Component
 +public class UserLogic extends AbstractAnyLogic<UserTO, UserMod> {
 +
 +    @Autowired
 +    protected UserDAO userDAO;
 +
 +    @Autowired
 +    protected GroupDAO groupDAO;
 +
 +    @Autowired
 +    protected AnySearchDAO searchDAO;
 +
 +    @Autowired
 +    protected UserDataBinder binder;
 +
 +    @Autowired
 +    protected VirAttrHandler virtAttrHandler;
 +
 +    @Autowired
 +    protected PropagationManager propagationManager;
 +
 +    @Autowired
 +    protected PropagationTaskExecutor taskExecutor;
 +
 +    @Autowired
 +    protected AnyTransformer anyTransformer;
 +
 +    @Autowired
 +    protected UserProvisioningManager provisioningManager;
 +
 +    @Autowired
 +    protected SyncopeLogic syncopeLogic;
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_READ + "')")
 +    @Transactional(readOnly = true)
 +    public String getUsername(final Long key) {
 +        return binder.getUserTO(key).getUsername();
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_READ + "')")
 +    @Transactional(readOnly = true)
 +    public Long getKey(final String username) {
 +        return binder.getUserTO(username).getKey();
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_LIST + "')")
 +    @Transactional(readOnly = true)
 +    @Override
 +    public int count(final List<String> realms) {
 +        return userDAO.count(
 +                getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.USER_LIST), realms));
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_LIST + "')")
 +    @Transactional(readOnly = true)
 +    @Override
 +    public List<UserTO> list(
 +            final int page, final int size, final List<OrderByClause> orderBy,
 +            final List<String> realms, final boolean details) {
 +
 +        return CollectionUtils.collect(userDAO.findAll(
 +                getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.USER_LIST), realms),
 +                page, size, orderBy),
 +                new Transformer<User, UserTO>() {
 +
 +                    @Override
 +                    public UserTO transform(final User input) {
 +                        return binder.getUserTO(input, details);
 +                    }
 +                }, new ArrayList<UserTO>());
 +    }
 +
 +    @PreAuthorize("isAuthenticated()")
 +    @Transactional(readOnly = true)
 +    public Pair<String, UserTO> readSelf() {
 +        return ImmutablePair.of(
 +                POJOHelper.serialize(AuthContextUtils.getAuthorizations()),
 +                binder.getAuthenticatedUserTO());
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_READ + "')")
 +    @Transactional(readOnly = true)
 +    @Override
 +    public UserTO read(final Long key) {
 +        return binder.getUserTO(key);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_SEARCH + "')")
 +    @Transactional(readOnly = true)
 +    @Override
 +    public int searchCount(final SearchCond searchCondition, final List<String> realms) {
 +        return searchDAO.count(
 +                getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.USER_SEARCH), realms),
 +                searchCondition, AnyTypeKind.USER);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_SEARCH + "')")
 +    @Transactional(readOnly = true)
 +    @Override
 +    public List<UserTO> search(final SearchCond searchCondition, final int page, final int size,
 +            final List<OrderByClause> orderBy, final List<String> realms, final boolean details) {
 +
 +        List<User> matchingUsers = searchDAO.search(
 +                getEffectiveRealms(AuthContextUtils.getAuthorizations().get(Entitlement.USER_SEARCH), realms),
 +                searchCondition, page, size, orderBy, AnyTypeKind.USER);
 +        return CollectionUtils.collect(matchingUsers, new Transformer<User, UserTO>() {
 +
 +            @Override
 +            public UserTO transform(final User input) {
 +                return binder.getUserTO(input, details);
 +            }
 +        }, new ArrayList<UserTO>());
 +    }
 +
 +    @PreAuthorize("isAnonymous() or hasRole('" + Entitlement.ANONYMOUS + "')")
 +    public UserTO selfCreate(final UserTO userTO, final boolean storePassword) {
 +        return doCreate(userTO, storePassword);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_CREATE + "')")
 +    @Override
 +    public UserTO create(final UserTO userTO) {
 +        return create(userTO, true);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_CREATE + "')")
 +    public UserTO create(final UserTO userTO, final boolean storePassword) {
 +        if (userTO.getRealm() == null) {
 +            throw SyncopeClientException.build(ClientExceptionType.InvalidRealm);
 +        }
 +        // security checks
 +        Set<String> effectiveRealms = getEffectiveRealms(
 +                AuthContextUtils.getAuthorizations().get(Entitlement.USER_CREATE),
 +                Collections.singleton(userTO.getRealm()));
 +        securityChecks(effectiveRealms, userTO.getRealm(), null);
 +
 +        return doCreate(userTO, storePassword);
 +    }
 +
 +    protected UserTO doCreate(final UserTO userTO, final boolean storePassword) {
 +        // Any transformation (if configured)
 +        UserTO actual = anyTransformer.transform(userTO);
 +        LOG.debug("Transformed: {}", actual);
 +
 +        Map.Entry<Long, List<PropagationStatus>> created = provisioningManager.create(actual, storePassword);
 +
 +        UserTO savedTO = binder.getUserTO(created.getKey());
 +        savedTO.getPropagationStatusTOs().addAll(created.getValue());
 +        return savedTO;
 +    }
 +
 +    @PreAuthorize("isAuthenticated() and not(hasRole('" + Entitlement.ANONYMOUS + "'))")
 +    public UserTO selfUpdate(final UserMod userMod) {
 +        UserTO userTO = binder.getAuthenticatedUserTO();
 +
 +        if (userTO.getKey() != userMod.getKey()) {
 +            throw new AccessControlException("Not allowed for user with key " + userMod.getKey());
 +        }
 +
 +        return doUpdate(userMod);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
 +    @Override
 +    public UserTO update(final UserMod userMod) {
 +        // Any transformation (if configured)
 +        UserMod actual = anyTransformer.transform(userMod);
 +        LOG.debug("Transformed: {}", actual);
 +
 +        // security checks
 +        UserTO toUpdate = binder.getUserTO(userMod.getKey());
 +        Set<String> requestedRealms = new HashSet<>();
 +        requestedRealms.add(toUpdate.getRealm());
 +        if (StringUtils.isNotBlank(actual.getRealm())) {
 +            requestedRealms.add(actual.getRealm());
 +        }
 +        Set<String> effectiveRealms = getEffectiveRealms(
 +                AuthContextUtils.getAuthorizations().get(Entitlement.USER_UPDATE),
 +                requestedRealms);
 +        securityChecks(effectiveRealms, toUpdate.getRealm(), toUpdate.getKey());
 +        if (StringUtils.isNotBlank(actual.getRealm())) {
 +            securityChecks(effectiveRealms, actual.getRealm(), toUpdate.getKey());
 +        }
 +
 +        return doUpdate(actual);
 +    }
 +
 +    protected UserTO doUpdate(final UserMod userMod) {
 +        Map.Entry<Long, List<PropagationStatus>> updated = provisioningManager.update(userMod);
 +
 +        UserTO updatedTO = binder.getUserTO(updated.getKey());
 +        updatedTO.getPropagationStatusTOs().addAll(updated.getValue());
 +        return updatedTO;
 +    }
 +
 +    protected Map.Entry<Long, List<PropagationStatus>> setStatusOnWfAdapter(final StatusMod statusMod) {
 +        Map.Entry<Long, List<PropagationStatus>> updated;
 +
 +        switch (statusMod.getType()) {
 +            case SUSPEND:
 +                updated = provisioningManager.suspend(statusMod);
 +                break;
 +
 +            case REACTIVATE:
 +                updated = provisioningManager.reactivate(statusMod);
 +                break;
 +
 +            case ACTIVATE:
 +            default:
 +                updated = provisioningManager.activate(statusMod);
 +                break;
 +
 +        }
 +
 +        return updated;
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
 +    public UserTO status(final StatusMod statusMod) {
 +        // security checks
 +        UserTO toUpdate = binder.getUserTO(statusMod.getKey());
 +        Set<String> effectiveRealms = getEffectiveRealms(
 +                AuthContextUtils.getAuthorizations().get(Entitlement.USER_UPDATE),
 +                Collections.singleton(toUpdate.getRealm()));
 +        securityChecks(effectiveRealms, toUpdate.getRealm(), toUpdate.getKey());
 +
 +        Map.Entry<Long, List<PropagationStatus>> updated = setStatusOnWfAdapter(statusMod);
 +        UserTO savedTO = binder.getUserTO(updated.getKey());
 +        savedTO.getPropagationStatusTOs().addAll(updated.getValue());
 +        return savedTO;
 +    }
 +
 +    @PreAuthorize("isAnonymous() or hasRole('" + Entitlement.ANONYMOUS + "')")
 +    @Transactional
 +    public void requestPasswordReset(final String username, final String securityAnswer) {
 +        if (username == null) {
 +            throw new NotFoundException("Null username");
 +        }
 +
 +        User user = userDAO.find(username);
 +        if (user == null) {
 +            throw new NotFoundException("User " + username);
 +        }
 +
 +        if (syncopeLogic.isPwdResetRequiringSecurityQuestions()
 +                && (securityAnswer == null || !securityAnswer.equals(user.getSecurityAnswer()))) {
 +
 +            throw SyncopeClientException.build(ClientExceptionType.InvalidSecurityAnswer);
 +        }
 +
 +        provisioningManager.requestPasswordReset(user.getKey());
 +    }
 +
 +    @PreAuthorize("isAnonymous() or hasRole('" + Entitlement.ANONYMOUS + "')")
 +    @Transactional
 +    public void confirmPasswordReset(final String token, final String password) {
 +        User user = userDAO.findByToken(token);
 +        if (user == null) {
 +            throw new NotFoundException("User with token " + token);
 +        }
 +        provisioningManager.confirmPasswordReset(user.getKey(), token, password);
 +    }
 +
 +    @PreAuthorize("isAuthenticated() and not(hasRole('" + Entitlement.ANONYMOUS + "'))")
 +    public UserTO selfDelete() {
 +        UserTO userTO = binder.getAuthenticatedUserTO();
 +
 +        return doDelete(userTO.getKey());
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_DELETE + "')")
 +    @Override
 +    public UserTO delete(final Long key) {
 +        // security checks
 +        UserTO toDelete = binder.getUserTO(key);
 +        Set<String> effectiveRealms = getEffectiveRealms(
 +                AuthContextUtils.getAuthorizations().get(Entitlement.USER_DELETE),
 +                Collections.singleton(toDelete.getRealm()));
 +        securityChecks(effectiveRealms, toDelete.getRealm(), toDelete.getKey());
 +
 +        return doDelete(key);
 +    }
 +
 +    protected UserTO doDelete(final Long key) {
 +        List<Group> ownedGroups = groupDAO.findOwnedByUser(key);
 +        if (!ownedGroups.isEmpty()) {
 +            SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.GroupOwnership);
 +            sce.getElements().addAll(CollectionUtils.collect(ownedGroups, new Transformer<Group, String>() {
 +
 +                @Override
 +                public String transform(final Group group) {
 +                    return group.getKey() + " " + group.getName();
 +                }
 +            }, new ArrayList<String>()));
 +            throw sce;
 +        }
 +
 +        List<PropagationStatus> statuses = provisioningManager.delete(key);
 +
 +        UserTO deletedTO;
 +        if (userDAO.find(key) == null) {
 +            deletedTO = new UserTO();
 +            deletedTO.setKey(key);
 +        } else {
 +            deletedTO = binder.getUserTO(key);
 +        }
 +        deletedTO.getPropagationStatusTOs().addAll(statuses);
 +
 +        return deletedTO;
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
 +    @Override
 +    public UserTO unlink(final Long key, final Collection<String> resources) {
 +        // security checks
 +        UserTO user = binder.getUserTO(key);
 +        Set<String> effectiveRealms = getEffectiveRealms(
 +                AuthContextUtils.getAuthorizations().get(Entitlement.USER_UPDATE),
 +                Collections.singleton(user.getRealm()));
 +        securityChecks(effectiveRealms, user.getRealm(), user.getKey());
 +
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(key);
 +        userMod.getResourcesToRemove().addAll(resources);
 +
 +        return binder.getUserTO(provisioningManager.unlink(userMod));
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
 +    @Override
 +    public UserTO link(final Long key, final Collection<String> resources) {
 +        // security checks
 +        UserTO user = binder.getUserTO(key);
 +        Set<String> effectiveRealms = getEffectiveRealms(
 +                AuthContextUtils.getAuthorizations().get(Entitlement.USER_UPDATE),
 +                Collections.singleton(user.getRealm()));
 +        securityChecks(effectiveRealms, user.getRealm(), user.getKey());
 +
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(key);
 +        userMod.getResourcesToAdd().addAll(resources);
 +
 +        return binder.getUserTO(provisioningManager.link(userMod));
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
 +    @Override
 +    public UserTO unassign(final Long key, final Collection<String> resources) {
 +        // security checks
 +        UserTO user = binder.getUserTO(key);
 +        Set<String> effectiveRealms = getEffectiveRealms(
 +                AuthContextUtils.getAuthorizations().get(Entitlement.USER_UPDATE),
 +                Collections.singleton(user.getRealm()));
 +        securityChecks(effectiveRealms, user.getRealm(), user.getKey());
 +
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(key);
 +        userMod.getResourcesToRemove().addAll(resources);
 +        return update(userMod);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
 +    @Override
 +    public UserTO assign(
 +            final Long key,
 +            final Collection<String> resources,
 +            final boolean changepwd,
 +            final String password) {
 +
 +        // security checks
 +        UserTO user = binder.getUserTO(key);
 +        Set<String> effectiveRealms = getEffectiveRealms(
 +                AuthContextUtils.getAuthorizations().get(Entitlement.USER_UPDATE),
 +                Collections.singleton(user.getRealm()));
 +        securityChecks(effectiveRealms, user.getRealm(), user.getKey());
 +
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(key);
 +        userMod.getResourcesToAdd().addAll(resources);
 +
 +        if (changepwd) {
 +            StatusMod statusMod = new StatusMod();
 +            statusMod.setOnSyncope(false);
-             statusMod.getResourceNames().addAll(resources);
++            statusMod.getResources().addAll(resources);
 +            userMod.setPwdPropRequest(statusMod);
 +            userMod.setPassword(password);
 +        }
 +
 +        return update(userMod);
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
 +    @Override
 +    public UserTO deprovision(final Long key, final Collection<String> resources) {
 +        // security checks
 +        UserTO user = binder.getUserTO(key);
 +        Set<String> effectiveRealms = getEffectiveRealms(
 +                AuthContextUtils.getAuthorizations().get(Entitlement.USER_UPDATE),
 +                Collections.singleton(user.getRealm()));
 +        securityChecks(effectiveRealms, user.getRealm(), user.getKey());
 +
 +        List<PropagationStatus> statuses = provisioningManager.deprovision(key, resources);
 +
 +        UserTO updatedTO = binder.getUserTO(key);
 +        updatedTO.getPropagationStatusTOs().addAll(statuses);
 +        return updatedTO;
 +    }
 +
 +    @PreAuthorize("hasRole('" + Entitlement.USER_UPDATE + "')")
 +    @Override
 +    public UserTO provision(
 +            final Long key,
 +            final Collection<String> resources,
 +            final boolean changePwd,
 +            final String password) {
 +
 +        // security checks
 +        UserTO user = binder.getUserTO(key);
 +        Set<String> effectiveRealms = getEffectiveRealms(
 +                AuthContextUtils.getAuthorizations().get(Entitlement.USER_UPDATE),
 +                Collections.singleton(user.getRealm()));
 +        securityChecks(effectiveRealms, user.getRealm(), user.getKey());
 +
 +        user.getPropagationStatusTOs().addAll(provisioningManager.provision(key, changePwd, password, resources));
 +        return user;
 +    }
 +
 +    @Override
 +    protected UserTO resolveReference(final Method method, final Object... args) throws UnresolvedReferenceException {
 +        Object key = null;
 +
 +        if (!"confirmPasswordReset".equals(method.getName()) && 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 String) {
 +                    key = (String) args[i];
 +                } else if (args[i] instanceof UserTO) {
 +                    key = ((UserTO) args[i]).getKey();
 +                } else if (args[i] instanceof UserMod) {
 +                    key = ((UserMod) args[i]).getKey();
 +                }
 +            }
 +        }
 +
 +        if ((key != null) && !key.equals(0L)) {
 +            try {
 +                return key instanceof Long ? binder.getUserTO((Long) key) : binder.getUserTO((String) 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/054ea9ca/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
index 38264de,0000000..5fd3ec1
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultUserProvisioningManager.java
@@@ -1,388 -1,0 +1,388 @@@
 +/*
 + * 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.provisioning.java;
 +
 +import java.util.Collection;
 +import java.util.Collections;
 +import java.util.HashSet;
 +import java.util.List;
 +import java.util.Set;
 +import org.apache.commons.collections4.CollectionUtils;
 +import org.apache.commons.lang3.tuple.ImmutablePair;
 +import org.apache.commons.lang3.tuple.Pair;
 +import org.apache.syncope.common.lib.mod.StatusMod;
 +import org.apache.syncope.common.lib.mod.UserMod;
 +import org.apache.syncope.common.lib.to.PropagationStatus;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.core.persistence.api.dao.UserDAO;
 +import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 +import org.apache.syncope.core.persistence.api.entity.user.User;
 +import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
 +import org.apache.syncope.core.provisioning.api.WorkflowResult;
 +import org.apache.syncope.common.lib.types.PropagationByResource;
 +import org.apache.syncope.common.lib.types.ResourceOperation;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationException;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
 +import org.apache.syncope.core.provisioning.api.sync.ProvisioningResult;
 +import org.apache.syncope.core.misc.spring.ApplicationContextProvider;
 +import org.apache.syncope.core.provisioning.api.VirAttrHandler;
 +import org.apache.syncope.core.workflow.api.UserWorkflowAdapter;
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +import org.springframework.beans.factory.annotation.Autowired;
 +
 +public class DefaultUserProvisioningManager implements UserProvisioningManager {
 +
 +    private static final Logger LOG = LoggerFactory.getLogger(UserProvisioningManager.class);
 +
 +    @Autowired
 +    protected UserWorkflowAdapter uwfAdapter;
 +
 +    @Autowired
 +    protected PropagationManager propagationManager;
 +
 +    @Autowired
 +    protected PropagationTaskExecutor taskExecutor;
 +
 +    @Autowired
 +    protected VirAttrHandler virtAttrHandler;
 +
 +    @Autowired
 +    protected UserDAO userDAO;
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> create(final UserTO userTO) {
 +        return create(userTO, true, false, null, Collections.<String>emptySet());
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> create(final UserTO userTO, final boolean storePassword) {
 +        return create(userTO, storePassword, false, null, Collections.<String>emptySet());
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> create(final UserTO userTO, final Set<String> excludedResources) {
 +        return create(userTO, false, false, null, excludedResources);
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> create(final UserTO userTO, final boolean storePassword,
 +            final boolean disablePwdPolicyCheck, final Boolean enabled, final Set<String> excludedResources) {
 +
 +        WorkflowResult<Pair<Long, Boolean>> created =
 +                uwfAdapter.create(userTO, disablePwdPolicyCheck, enabled, storePassword);
 +
 +        List<PropagationTask> tasks = propagationManager.getUserCreateTasks(
 +                created.getResult().getKey(),
 +                created.getResult().getValue(),
 +                created.getPropByRes(),
 +                userTO.getPassword(),
 +                userTO.getVirAttrs(),
 +                excludedResources);
 +        PropagationReporter propagationReporter =
 +                ApplicationContextProvider.getBeanFactory().getBean(PropagationReporter.class);
 +        try {
 +            taskExecutor.execute(tasks, propagationReporter);
 +        } catch (PropagationException e) {
 +            LOG.error("Error propagation primary resource", e);
 +            propagationReporter.onPrimaryResourceFailure(tasks);
 +        }
 +
 +        return new ImmutablePair<>(created.getResult().getKey(), propagationReporter.getStatuses());
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> update(final UserMod userMod) {
 +        WorkflowResult<Pair<UserMod, Boolean>> updated = uwfAdapter.update(userMod);
 +
 +        List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(updated);
 +        if (tasks.isEmpty()) {
 +            // SYNCOPE-459: take care of user virtual attributes ...
 +            PropagationByResource propByResVirAttr = virtAttrHandler.fillVirtual(
 +                    updated.getResult().getKey().getKey(),
 +                    AnyTypeKind.USER,
 +                    userMod.getVirAttrsToRemove(),
 +                    userMod.getVirAttrsToUpdate());
 +            tasks.addAll(!propByResVirAttr.isEmpty()
 +                    ? propagationManager.getUserUpdateTasks(updated, false, null)
 +                    : Collections.<PropagationTask>emptyList());
 +        }
 +        PropagationReporter propagationReporter = ApplicationContextProvider.getBeanFactory().
 +                getBean(PropagationReporter.class);
 +        if (!tasks.isEmpty()) {
 +            try {
 +                taskExecutor.execute(tasks, propagationReporter);
 +            } catch (PropagationException e) {
 +                LOG.error("Error propagation primary resource", e);
 +                propagationReporter.onPrimaryResourceFailure(tasks);
 +            }
 +        }
 +
 +        return new ImmutablePair<>(updated.getResult().getKey().getKey(), propagationReporter.getStatuses());
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> update(final UserMod userMod, final Set<String> excludedResources) {
 +        return update(userMod, userMod.getKey(), new ProvisioningResult(), null, excludedResources);
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> update(final UserMod userMod, final Long key,
 +            final ProvisioningResult result, final Boolean enabled, final Set<String> excludedResources) {
 +
 +        WorkflowResult<Pair<UserMod, Boolean>> updated;
 +        try {
 +            updated = uwfAdapter.update(userMod);
 +        } catch (Exception e) {
 +            LOG.error("Update of user {} failed, trying to sync its status anyway (if configured)", key, e);
 +
 +            result.setStatus(ProvisioningResult.Status.FAILURE);
 +            result.setMessage("Update failed, trying to sync status anyway (if configured)\n" + e.getMessage());
 +
 +            updated = new WorkflowResult<Pair<UserMod, Boolean>>(
 +                    new ImmutablePair<>(userMod, false), new PropagationByResource(),
 +                    new HashSet<String>());
 +        }
 +
 +        if (enabled != null) {
 +            User user = userDAO.find(key);
 +
 +            WorkflowResult<Long> enableUpdate = null;
 +            if (user.isSuspended() == null) {
 +                enableUpdate = uwfAdapter.activate(key, null);
 +            } else if (enabled && user.isSuspended()) {
 +                enableUpdate = uwfAdapter.reactivate(key);
 +            } else if (!enabled && !user.isSuspended()) {
 +                enableUpdate = uwfAdapter.suspend(key);
 +            }
 +
 +            if (enableUpdate != null) {
 +                if (enableUpdate.getPropByRes() != null) {
 +                    updated.getPropByRes().merge(enableUpdate.getPropByRes());
 +                    updated.getPropByRes().purge();
 +                }
 +                updated.getPerformedTasks().addAll(enableUpdate.getPerformedTasks());
 +            }
 +        }
 +
 +        List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(
 +                updated, updated.getResult().getKey().getPassword() != null, excludedResources);
 +        PropagationReporter propagationReporter = ApplicationContextProvider.getBeanFactory().
 +                getBean(PropagationReporter.class);
 +        try {
 +            taskExecutor.execute(tasks, propagationReporter);
 +        } catch (PropagationException e) {
 +            LOG.error("Error propagation primary resource", e);
 +            propagationReporter.onPrimaryResourceFailure(tasks);
 +        }
 +
 +        return new ImmutablePair<>(updated.getResult().getKey().getKey(), propagationReporter.getStatuses());
 +    }
 +
 +    @Override
 +    public List<PropagationStatus> delete(final Long key) {
 +        return delete(key, Collections.<String>emptySet());
 +    }
 +
 +    @Override
 +    public List<PropagationStatus> delete(final Long key, final Set<String> excludedResources) {
 +        // Note here that we can only notify about "delete", not any other
 +        // task defined in workflow process definition: this because this
 +        // information could only be available after uwfAdapter.delete(), which
 +        // will also effectively remove user from db, thus making virtually
 +        // impossible by NotificationManager to fetch required user information
 +        List<PropagationTask> tasks = propagationManager.getUserDeleteTasks(key, excludedResources);
 +
 +        PropagationReporter propagationReporter =
 +                ApplicationContextProvider.getBeanFactory().getBean(PropagationReporter.class);
 +        try {
 +            taskExecutor.execute(tasks, propagationReporter);
 +        } catch (PropagationException e) {
 +            LOG.error("Error propagation primary resource", e);
 +            propagationReporter.onPrimaryResourceFailure(tasks);
 +        }
 +
 +        try {
 +            uwfAdapter.delete(key);
 +        } catch (PropagationException e) {
 +            throw e;
 +        }
 +
 +        return propagationReporter.getStatuses();
 +    }
 +
 +    @Override
 +    public Long unlink(final UserMod userMod) {
 +        WorkflowResult<Pair<UserMod, Boolean>> updated = uwfAdapter.update(userMod);
 +        return updated.getResult().getKey().getKey();
 +    }
 +
 +    @Override
 +    public Long link(final UserMod userMod) {
 +        return uwfAdapter.update(userMod).getResult().getKey().getKey();
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> activate(final StatusMod statusMod) {
 +        WorkflowResult<Long> updated = statusMod.isOnSyncope()
 +                ? uwfAdapter.activate(statusMod.getKey(), statusMod.getToken())
 +                : new WorkflowResult<>(statusMod.getKey(), null, statusMod.getType().name().toLowerCase());
 +
 +        return new ImmutablePair<>(updated.getResult(), propagateStatus(statusMod));
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> reactivate(final StatusMod statusMod) {
 +        WorkflowResult<Long> updated = statusMod.isOnSyncope()
 +                ? uwfAdapter.reactivate(statusMod.getKey())
 +                : new WorkflowResult<>(statusMod.getKey(), null, statusMod.getType().name().toLowerCase());
 +
 +        return new ImmutablePair<>(updated.getResult(), propagateStatus(statusMod));
 +    }
 +
 +    @Override
 +    public Pair<Long, List<PropagationStatus>> suspend(final StatusMod statusMod) {
 +        WorkflowResult<Long> updated = statusMod.isOnSyncope()
 +                ? uwfAdapter.suspend(statusMod.getKey())
 +                : new WorkflowResult<>(statusMod.getKey(), null, statusMod.getType().name().toLowerCase());
 +
 +        return new ImmutablePair<>(updated.getResult(), propagateStatus(statusMod));
 +    }
 +
 +    protected List<PropagationStatus> propagateStatus(final StatusMod statusMod) {
 +        Collection<String> noPropResourceNames = CollectionUtils.removeAll(
-                 userDAO.findAllResourceNames(userDAO.find(statusMod.getKey())), statusMod.getResourceNames());
++                userDAO.findAllResourceNames(userDAO.find(statusMod.getKey())), statusMod.getResources());
 +
 +        List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(
 +                statusMod.getKey(), statusMod.getType() != StatusMod.ModType.SUSPEND, noPropResourceNames);
 +        PropagationReporter propReporter =
 +                ApplicationContextProvider.getBeanFactory().getBean(PropagationReporter.class);
 +        try {
 +            taskExecutor.execute(tasks, propReporter);
 +        } catch (PropagationException e) {
 +            LOG.error("Error propagation primary resource", e);
 +            propReporter.onPrimaryResourceFailure(tasks);
 +        }
 +
 +        return propReporter.getStatuses();
 +
 +    }
 +
 +    @Override
 +    public void internalSuspend(final Long key) {
 +        Pair<WorkflowResult<Long>, Boolean> updated = uwfAdapter.internalSuspend(key);
 +
 +        // propagate suspension if and only if it is required by policy
 +        if (updated != null && updated.getValue()) {
 +            UserMod userMod = new UserMod();
 +            userMod.setKey(updated.getKey().getResult());
 +
 +            List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(
 +                    new WorkflowResult<Pair<UserMod, Boolean>>(
 +                            new ImmutablePair<>(userMod, Boolean.FALSE),
 +                            updated.getKey().getPropByRes(), updated.getKey().getPerformedTasks()));
 +            taskExecutor.execute(tasks);
 +        }
 +    }
 +
 +    @Override
 +    public List<PropagationStatus> provision(
 +            final Long key, final boolean changePwd, final String password, final Collection<String> resources) {
 +
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(key);
 +        userMod.getResourcesToAdd().addAll(resources);
 +
 +        if (changePwd) {
 +            StatusMod statusMod = new StatusMod();
 +            statusMod.setOnSyncope(false);
-             statusMod.getResourceNames().addAll(resources);
++            statusMod.getResources().addAll(resources);
 +            userMod.setPwdPropRequest(statusMod);
 +            userMod.setPassword(password);
 +        }
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.addAll(ResourceOperation.UPDATE, resources);
 +
 +        WorkflowResult<Pair<UserMod, Boolean>> wfResult = new WorkflowResult<Pair<UserMod, Boolean>>(
 +                ImmutablePair.of(userMod, (Boolean) null), propByRes, "update");
 +
 +        List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(wfResult, changePwd, null);
 +        PropagationReporter propagationReporter =
 +                ApplicationContextProvider.getBeanFactory().getBean(PropagationReporter.class);
 +        try {
 +            taskExecutor.execute(tasks, propagationReporter);
 +        } catch (PropagationException e) {
 +            LOG.error("Error propagation primary resource", e);
 +            propagationReporter.onPrimaryResourceFailure(tasks);
 +        }
 +
 +        return propagationReporter.getStatuses();
 +    }
 +
 +    @Override
 +    public List<PropagationStatus> deprovision(final Long key, final Collection<String> resources) {
 +        User user = userDAO.authFind(key);
 +
 +        List<PropagationTask> tasks = propagationManager.getUserDeleteTasks(
 +                key,
 +                new HashSet<>(resources),
 +                CollectionUtils.removeAll(userDAO.findAllResourceNames(user), resources));
 +        PropagationReporter propagationReporter =
 +                ApplicationContextProvider.getBeanFactory().getBean(PropagationReporter.class);
 +        try {
 +            taskExecutor.execute(tasks, propagationReporter);
 +        } catch (PropagationException e) {
 +            LOG.error("Error propagation primary resource", e);
 +            propagationReporter.onPrimaryResourceFailure(tasks);
 +        }
 +
 +        return propagationReporter.getStatuses();
 +    }
 +
 +    @Override
 +    public void requestPasswordReset(final Long key) {
 +        uwfAdapter.requestPasswordReset(key);
 +    }
 +
 +    @Override
 +    public void confirmPasswordReset(final Long key, final String token, final String password) {
 +        uwfAdapter.confirmPasswordReset(key, token, password);
 +
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(key);
 +        userMod.setPassword(password);
 +
 +        List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(
 +                new WorkflowResult<Pair<UserMod, Boolean>>(
 +                        new ImmutablePair<UserMod, Boolean>(userMod, null), null, "confirmPasswordReset"),
 +                true, null);
 +        PropagationReporter propReporter =
 +                ApplicationContextProvider.getBeanFactory().getBean(PropagationReporter.class);
 +        try {
 +            taskExecutor.execute(tasks, propReporter);
 +        } catch (PropagationException e) {
 +            LOG.error("Error propagation primary resource", e);
 +            propReporter.onPrimaryResourceFailure(tasks);
 +        }
 +    }
 +}


[28/33] syncope git commit: [SYNCOPE-686] Merge from 1_2_X

Posted by md...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/054ea9ca/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
index 459c052,0000000..2e1224b
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
@@@ -1,477 -1,0 +1,517 @@@
 +/*
 + * 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.provisioning.java.data;
 +
 +import java.util.Collection;
 +import java.util.Date;
 +import java.util.HashSet;
 +import java.util.Map;
 +import java.util.Set;
 +import javax.annotation.Resource;
 +import org.apache.commons.collections4.Closure;
 +import org.apache.commons.collections4.CollectionUtils;
++import org.apache.commons.collections4.Predicate;
 +import org.apache.commons.collections4.Transformer;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.syncope.common.lib.SyncopeClientCompositeException;
 +import org.apache.syncope.common.lib.SyncopeClientException;
++import org.apache.syncope.common.lib.mod.StatusMod;
 +import org.apache.syncope.common.lib.mod.UserMod;
 +import org.apache.syncope.common.lib.to.MembershipTO;
 +import org.apache.syncope.common.lib.to.RelationshipTO;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.common.lib.types.CipherAlgorithm;
 +import org.apache.syncope.common.lib.types.ClientExceptionType;
 +import org.apache.syncope.common.lib.types.ResourceOperation;
 +import org.apache.syncope.core.persistence.api.dao.ConfDAO;
 +import org.apache.syncope.core.persistence.api.dao.SecurityQuestionDAO;
 +import org.apache.syncope.core.persistence.api.entity.group.Group;
 +import org.apache.syncope.core.persistence.api.entity.user.SecurityQuestion;
 +import org.apache.syncope.core.persistence.api.entity.user.User;
 +import org.apache.syncope.common.lib.types.PropagationByResource;
 +import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
 +import org.apache.syncope.core.misc.security.AuthContextUtils;
 +import org.apache.syncope.core.misc.security.Encryptor;
 +import org.apache.syncope.core.misc.spring.BeanUtils;
++import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
 +import org.apache.syncope.core.persistence.api.dao.RoleDAO;
 +import org.apache.syncope.core.persistence.api.entity.Role;
 +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
++import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
++import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
++import org.apache.syncope.core.persistence.api.entity.resource.Provision;
 +import org.apache.syncope.core.persistence.api.entity.user.UMembership;
 +import org.apache.syncope.core.persistence.api.entity.user.URelationship;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.stereotype.Component;
 +import org.springframework.transaction.annotation.Transactional;
 +
 +@Component
 +@Transactional(rollbackFor = { Throwable.class })
 +public class UserDataBinderImpl extends AbstractAnyDataBinder implements UserDataBinder {
 +
 +    private static final String[] IGNORE_PROPERTIES = {
 +        "type", "realm", "auxClasses", "roles", "dynRoles", "relationships", "memberships", "dynGroups",
 +        "plainAttrs", "derAttrs", "virAttrs", "resources", "securityQuestion", "securityAnswer"
 +    };
 +
 +    @Autowired
 +    private RoleDAO roleDAO;
 +
 +    @Autowired
 +    private ConfDAO confDAO;
 +
 +    @Autowired
 +    private SecurityQuestionDAO securityQuestionDAO;
 +
++    @Autowired
++    private AnyTypeDAO anyTypeDAO;
++
 +    @Resource(name = "adminUser")
 +    private String adminUser;
 +
 +    @Resource(name = "anonymousUser")
 +    private String anonymousUser;
 +
 +    private final Encryptor encryptor = Encryptor.getInstance();
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public UserTO getAuthenticatedUserTO() {
 +        final UserTO authUserTO;
 +
 +        String authUsername = AuthContextUtils.getUsername();
 +        if (anonymousUser.equals(authUsername)) {
 +            authUserTO = new UserTO();
 +            authUserTO.setKey(-2);
 +            authUserTO.setUsername(anonymousUser);
 +        } else if (adminUser.equals(authUsername)) {
 +            authUserTO = new UserTO();
 +            authUserTO.setKey(-1);
 +            authUserTO.setUsername(adminUser);
 +        } else {
 +            User authUser = userDAO.find(authUsername);
 +            authUserTO = getUserTO(authUser, true);
 +        }
 +
 +        return authUserTO;
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public boolean verifyPassword(final String username, final String password) {
 +        return verifyPassword(userDAO.authFind(username), password);
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public boolean verifyPassword(final User user, final String password) {
 +        return encryptor.verify(password, user.getCipherAlgorithm(), user.getPassword());
 +    }
 +
 +    private void setPassword(final User user, final String password, final SyncopeClientCompositeException scce) {
 +        try {
 +            final String algorithm = confDAO.find(
 +                    "password.cipher.algorithm", CipherAlgorithm.AES.name()).getValues().get(0).getStringValue();
 +            CipherAlgorithm predefined = CipherAlgorithm.valueOf(algorithm);
 +            user.setPassword(password, predefined);
 +        } catch (IllegalArgumentException e) {
 +            final SyncopeClientException invalidCiperAlgorithm =
 +                    SyncopeClientException.build(ClientExceptionType.NotFound);
 +            invalidCiperAlgorithm.getElements().add(e.getMessage());
 +            scce.addException(invalidCiperAlgorithm);
 +
 +            throw scce;
 +        }
 +    }
 +
 +    @Override
 +    public void create(final User user, final UserTO userTO, final boolean storePassword) {
 +        SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
 +
 +        // roles
 +        for (Long roleKey : userTO.getRoles()) {
 +            Role role = roleDAO.find(roleKey);
 +            if (role == null) {
 +                LOG.warn("Ignoring unknown role with id {}", roleKey);
 +            } else {
 +                user.add(role);
 +            }
 +        }
 +
 +        // relationships
 +        for (RelationshipTO relationshipTO : userTO.getRelationships()) {
 +            AnyObject anyObject = anyObjectDAO.find(relationshipTO.getRightKey());
 +
 +            if (anyObject == null) {
 +                if (LOG.isDebugEnabled()) {
 +                    LOG.debug("Ignoring invalid anyObject " + relationshipTO.getRightKey());
 +                }
 +            } else {
 +                URelationship relationship = null;
 +                if (user.getKey() != null) {
 +                    relationship = user.getRelationship(anyObject.getKey());
 +                }
 +                if (relationship == null) {
 +                    relationship = entityFactory.newEntity(URelationship.class);
 +                    relationship.setRightEnd(anyObject);
 +                    relationship.setLeftEnd(user);
 +
 +                    user.add(relationship);
 +                }
 +            }
 +        }
 +
 +        // memberships
 +        for (MembershipTO membershipTO : userTO.getMemberships()) {
 +            Group group = groupDAO.find(membershipTO.getRightKey());
 +
 +            if (group == null) {
 +                if (LOG.isDebugEnabled()) {
 +                    LOG.debug("Ignoring invalid group " + membershipTO.getGroupName());
 +                }
 +            } else {
 +                UMembership membership = null;
 +                if (user.getKey() != null) {
 +                    membership = user.getMembership(group.getKey());
 +                }
 +                if (membership == null) {
 +                    membership = entityFactory.newEntity(UMembership.class);
 +                    membership.setRightEnd(group);
 +                    membership.setLeftEnd(user);
 +
 +                    user.add(membership);
 +                }
 +            }
 +        }
 +
 +        // realm, attributes, derived attributes, virtual attributes and resources
 +        fill(user, userTO, anyUtilsFactory.getInstance(AnyTypeKind.USER), scce);
 +
 +        // set password
 +        if (StringUtils.isBlank(userTO.getPassword()) || !storePassword) {
 +            LOG.debug("Password was not provided or not required to be stored");
 +        } else {
 +            setPassword(user, userTO.getPassword(), scce);
 +        }
 +
 +        // set username
 +        user.setUsername(userTO.getUsername());
 +
 +        // security question / answer
 +        if (userTO.getSecurityQuestion() != null) {
 +            SecurityQuestion securityQuestion = securityQuestionDAO.find(userTO.getSecurityQuestion());
 +            if (securityQuestion != null) {
 +                user.setSecurityQuestion(securityQuestion);
 +            }
 +        }
 +        user.setSecurityAnswer(userTO.getSecurityAnswer());
 +    }
 +
++    private boolean isPasswordMapped(final ExternalResource resource) {
++        boolean result = false;
++
++        Provision provision = resource.getProvision(anyTypeDAO.findUser());
++        if (provision != null && provision.getMapping() != null) {
++            result = CollectionUtils.exists(provision.getMapping().getItems(), new Predicate<MappingItem>() {
++
++                @Override
++                public boolean evaluate(final MappingItem item) {
++                    return item.isPassword();
++                }
++            });
++        }
++
++        return result;
++    }
++
 +    @Override
 +    public PropagationByResource update(final User toBeUpdated, final UserMod userMod) {
 +        // Re-merge any pending change from workflow tasks
 +        final User user = userDAO.save(toBeUpdated);
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +
 +        SyncopeClientCompositeException scce = SyncopeClientException.buildComposite();
 +
 +        Collection<String> currentResources = userDAO.findAllResourceNames(user);
 +
 +        // fetch connObjectKeys before update
 +        Map<String, String> oldConnObjectKeys = getConnObjectKeys(user);
 +
 +        // realm
 +        setRealm(user, userMod);
 +
 +        // password
 +        if (StringUtils.isNotBlank(userMod.getPassword())) {
 +            setPassword(user, userMod.getPassword(), scce);
 +            user.setChangePwdDate(new Date());
 +            propByRes.addAll(ResourceOperation.UPDATE, currentResources);
 +        }
 +
 +        // username
 +        if (userMod.getUsername() != null && !userMod.getUsername().equals(user.getUsername())) {
 +            propByRes.addAll(ResourceOperation.UPDATE, currentResources);
 +
 +            String oldUsername = user.getUsername();
 +            user.setUsername(userMod.getUsername());
 +
 +            if (oldUsername.equals(AuthContextUtils.getUsername())) {
 +                AuthContextUtils.updateUsername(userMod.getUsername());
 +            }
 +        }
 +
 +        // security question / answer:
 +        // userMod.getSecurityQuestion() is null => remove user security question and answer
 +        // userMod.getSecurityQuestion() == 0 => don't change anything
 +        // userMod.getSecurityQuestion() > 0 => update user security question and answer
 +        if (userMod.getSecurityQuestion() == null) {
 +            user.setSecurityQuestion(null);
 +            user.setSecurityAnswer(null);
 +        } else if (userMod.getSecurityQuestion() > 0) {
 +            SecurityQuestion securityQuestion = securityQuestionDAO.find(userMod.getSecurityQuestion());
 +            if (securityQuestion != null) {
 +                user.setSecurityQuestion(securityQuestion);
 +                user.setSecurityAnswer(userMod.getSecurityAnswer());
 +            }
 +        }
 +
 +        // roles
 +        CollectionUtils.forAllDo(userMod.getRolesToRemove(), new Closure<Long>() {
 +
 +            @Override
 +            public void execute(final Long roleKey) {
 +                Role role = roleDAO.find(roleKey);
 +                if (role == null) {
 +                    LOG.warn("Ignoring unknown role with id {}", roleKey);
 +                } else {
 +                    user.remove(role);
 +                }
 +            }
 +        });
 +        CollectionUtils.forAllDo(userMod.getRolesToAdd(), new Closure<Long>() {
 +
 +            @Override
 +            public void execute(final Long roleKey) {
 +                Role role = roleDAO.find(roleKey);
 +                if (role == null) {
 +                    LOG.warn("Ignoring unknown role with id {}", roleKey);
 +                } else {
 +                    user.add(role);
 +                }
 +            }
 +        });
 +
 +        // attributes, derived attributes, virtual attributes and resources
 +        propByRes.merge(fill(user, userMod, anyUtilsFactory.getInstance(AnyTypeKind.USER), scce));
 +
 +        Set<String> toBeDeprovisioned = new HashSet<>();
 +        Set<String> toBeProvisioned = new HashSet<>();
 +
 +        // relationships to be removed
 +        for (Long anyObjectKey : userMod.getRelationshipsToRemove()) {
 +            LOG.debug("Relationship to be removed for any object {}", anyObjectKey);
 +
 +            URelationship relationship = user.getRelationship(anyObjectKey);
 +            if (relationship == null) {
 +                LOG.warn("Invalid anyObject key specified for relationship to be removed: {}", anyObjectKey);
 +            } else {
 +                if (!userMod.getRelationshipsToAdd().contains(anyObjectKey)) {
 +                    user.remove(relationship);
 +                    toBeDeprovisioned.addAll(relationship.getRightEnd().getResourceNames());
 +                }
 +            }
 +        }
 +
 +        // relationships to be added
 +        for (Long anyObjectKey : userMod.getRelationshipsToAdd()) {
 +            LOG.debug("Relationship to be added for any object {}", anyObjectKey);
 +
 +            AnyObject otherEnd = anyObjectDAO.find(anyObjectKey);
 +            if (otherEnd == null) {
 +                LOG.debug("Ignoring invalid any object {}", anyObjectKey);
 +            } else {
 +                URelationship relationship = user.getRelationship(otherEnd.getKey());
 +                if (relationship == null) {
 +                    relationship = entityFactory.newEntity(URelationship.class);
 +                    relationship.setRightEnd(otherEnd);
 +                    relationship.setLeftEnd(user);
 +
 +                    user.add(relationship);
 +
 +                    toBeProvisioned.addAll(otherEnd.getResourceNames());
 +                }
 +            }
 +        }
 +
 +        // memberships to be removed
 +        for (Long groupKey : userMod.getMembershipsToRemove()) {
 +            LOG.debug("Membership to be removed for group {}", groupKey);
 +
 +            UMembership membership = user.getMembership(groupKey);
 +            if (membership == null) {
 +                LOG.debug("Invalid group key specified for membership to be removed: {}", groupKey);
 +            } else {
 +                if (!userMod.getMembershipsToAdd().contains(groupKey)) {
 +                    user.remove(membership);
 +                    toBeDeprovisioned.addAll(membership.getRightEnd().getResourceNames());
 +                }
 +            }
 +        }
 +
 +        // memberships to be added
 +        for (Long groupKey : userMod.getMembershipsToAdd()) {
 +            LOG.debug("Membership to be added for group {}", groupKey);
 +
 +            Group group = groupDAO.find(groupKey);
 +            if (group == null) {
 +                LOG.debug("Ignoring invalid group {}", groupKey);
 +            } else {
 +                UMembership membership = user.getMembership(group.getKey());
 +                if (membership == null) {
 +                    membership = entityFactory.newEntity(UMembership.class);
 +                    membership.setRightEnd(group);
 +                    membership.setLeftEnd(user);
 +
 +                    user.add(membership);
 +
 +                    toBeProvisioned.addAll(group.getResourceNames());
++
++                    // SYNCOPE-686: if password is invertible and we are adding resources with password mapping,
++                    // ensure that they are counted for password propagation
++                    if (toBeUpdated.canDecodePassword()) {
++                        for (ExternalResource resource : group.getResources()) {
++                            if (isPasswordMapped(resource)) {
++                                if (userMod.getPwdPropRequest() == null) {
++                                    userMod.setPwdPropRequest(new StatusMod());
++                                }
++
++                                userMod.getPwdPropRequest().getResources().add(resource.getKey());
++                            }
++                        }
++                    }
 +                }
 +            }
 +        }
 +
 +        propByRes.addAll(ResourceOperation.DELETE, toBeDeprovisioned);
 +        propByRes.addAll(ResourceOperation.UPDATE, toBeProvisioned);
 +
 +        // In case of new memberships all current resources need to be updated in order to propagate new group
 +        // attribute values.
 +        if (!toBeDeprovisioned.isEmpty() || !toBeProvisioned.isEmpty()) {
 +            currentResources.removeAll(toBeDeprovisioned);
 +            propByRes.addAll(ResourceOperation.UPDATE, currentResources);
 +        }
 +
 +        // check if some connObjectKey was changed by the update above
 +        Map<String, String> newcCnnObjectKeys = getConnObjectKeys(user);
 +        for (Map.Entry<String, String> entry : oldConnObjectKeys.entrySet()) {
 +            if (newcCnnObjectKeys.containsKey(entry.getKey())
 +                    && !entry.getValue().equals(newcCnnObjectKeys.get(entry.getKey()))) {
 +
 +                propByRes.addOldConnObjectKey(entry.getKey(), entry.getValue());
 +                propByRes.add(ResourceOperation.UPDATE, entry.getKey());
 +            }
 +        }
 +
 +        return propByRes;
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public UserTO getUserTO(final User user, final boolean details) {
 +        UserTO userTO = new UserTO();
 +
 +        BeanUtils.copyProperties(user, userTO, IGNORE_PROPERTIES);
 +
 +        if (user.getSecurityQuestion() != null) {
 +            userTO.setSecurityQuestion(user.getSecurityQuestion().getKey());
 +        }
 +
 +        if (details) {
 +            virAttrHander.retrieveVirAttrValues(user);
 +        }
 +
 +        fillTO(userTO, user.getRealm().getFullPath(), user.getAuxClasses(),
 +                user.getPlainAttrs(), user.getDerAttrs(), user.getVirAttrs(), userDAO.findAllResources(user));
 +
 +        if (details) {
 +            // roles
 +            CollectionUtils.collect(user.getRoles(), new Transformer<Role, Long>() {
 +
 +                @Override
 +                public Long transform(final Role role) {
 +                    return role.getKey();
 +                }
 +            }, userTO.getRoles());
 +
 +            // relationships
 +            CollectionUtils.collect(user.getRelationships(), new Transformer<URelationship, RelationshipTO>() {
 +
 +                @Override
 +                public RelationshipTO transform(final URelationship relationship) {
 +                    return UserDataBinderImpl.this.getRelationshipTO(relationship);
 +                }
 +
 +            }, userTO.getRelationships());
 +
 +            // memberships
 +            CollectionUtils.collect(user.getMemberships(), new Transformer<UMembership, MembershipTO>() {
 +
 +                @Override
 +                public MembershipTO transform(final UMembership membership) {
 +                    return UserDataBinderImpl.this.getMembershipTO(membership);
 +                }
 +            }, userTO.getMemberships());
 +
 +            // dynamic memberships
 +            CollectionUtils.collect(userDAO.findDynRoleMemberships(user), new Transformer<Role, Long>() {
 +
 +                @Override
 +                public Long transform(final Role role) {
 +                    return role.getKey();
 +                }
 +            }, userTO.getDynRoles());
 +            CollectionUtils.collect(userDAO.findDynGroupMemberships(user), new Transformer<Group, Long>() {
 +
 +                @Override
 +                public Long transform(final Group group) {
 +                    return group.getKey();
 +                }
 +            }, userTO.getDynGroups());
 +        }
 +
 +        return userTO;
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public UserTO getUserTO(final String username) {
 +        return getUserTO(userDAO.authFind(username), true);
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public UserTO getUserTO(final Long key) {
 +        return getUserTO(userDAO.authFind(key), true);
 +    }
 +
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/054ea9ca/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
----------------------------------------------------------------------
diff --cc core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
index 1c704df,0000000..631cbe3
mode 100644,000000..100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/PropagationManagerImpl.java
@@@ -1,498 -1,0 +1,498 @@@
 +/*
 + * 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.provisioning.java.propagation;
 +
 +import java.util.ArrayList;
 +import java.util.Collection;
 +import java.util.Collections;
 +import java.util.HashMap;
 +import java.util.HashSet;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Set;
 +import org.apache.commons.lang3.tuple.Pair;
 +import org.apache.syncope.common.lib.mod.AttrMod;
 +import org.apache.syncope.common.lib.mod.UserMod;
 +import org.apache.syncope.common.lib.to.AttrTO;
 +import org.apache.syncope.common.lib.types.MappingPurpose;
 +import org.apache.syncope.common.lib.types.PropagationByResource;
 +import org.apache.syncope.common.lib.types.ResourceOperation;
 +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO;
 +import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 +import org.apache.syncope.core.persistence.api.dao.UserDAO;
 +import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 +import org.apache.syncope.core.persistence.api.entity.VirAttr;
 +import org.apache.syncope.core.persistence.api.entity.group.Group;
 +import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 +import org.apache.syncope.core.persistence.api.entity.user.User;
 +import org.apache.syncope.core.provisioning.api.WorkflowResult;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
 +import org.apache.syncope.core.misc.ConnObjectUtils;
 +import org.apache.syncope.core.misc.MappingUtils;
 +import org.apache.syncope.core.misc.jexl.JexlUtils;
 +import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 +import org.apache.syncope.core.persistence.api.entity.Any;
 +import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 +import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
 +import org.apache.syncope.core.persistence.api.entity.resource.MappingItem;
 +import org.apache.syncope.core.persistence.api.entity.resource.Provision;
 +import org.apache.syncope.core.provisioning.api.VirAttrHandler;
 +import org.identityconnectors.framework.common.objects.Attribute;
 +import org.identityconnectors.framework.common.objects.AttributeBuilder;
 +import org.identityconnectors.framework.common.objects.AttributeUtil;
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.transaction.annotation.Transactional;
 +
 +/**
 + * Manage the data propagation to external resources.
 + */
 +@Transactional(rollbackFor = { Throwable.class })
 +public class PropagationManagerImpl implements PropagationManager {
 +
 +    protected static final Logger LOG = LoggerFactory.getLogger(PropagationManager.class);
 +
 +    @Autowired
 +    protected AnyObjectDAO anyObjectDAO;
 +
 +    /**
 +     * User DAO.
 +     */
 +    @Autowired
 +    protected UserDAO userDAO;
 +
 +    /**
 +     * Group DAO.
 +     */
 +    @Autowired
 +    protected GroupDAO groupDAO;
 +
 +    /**
 +     * Resource DAO.
 +     */
 +    @Autowired
 +    protected ExternalResourceDAO resourceDAO;
 +
 +    @Autowired
 +    protected EntityFactory entityFactory;
 +
 +    /**
 +     * ConnObjectUtils.
 +     */
 +    @Autowired
 +    protected ConnObjectUtils connObjectUtils;
 +
 +    @Autowired
 +    protected VirAttrHandler virAttrHandler;
 +
 +    @Override
 +    public List<PropagationTask> getAnyObjectCreateTasks(
 +            final WorkflowResult<Long> wfResult,
 +            final Collection<AttrTO> vAttrs,
 +            final Collection<String> noPropResourceNames) {
 +
 +        return getAnyObjectCreateTasks(wfResult.getResult(), vAttrs, wfResult.getPropByRes(), noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getAnyObjectCreateTasks(
 +            final Long key,
 +            final Collection<AttrTO> vAttrs,
 +            final PropagationByResource propByRes,
 +            final Collection<String> noPropResourceNames) {
 +
 +        AnyObject anyObject = anyObjectDAO.authFind(key);
 +        if (vAttrs != null && !vAttrs.isEmpty()) {
 +            virAttrHandler.fillVirtual(anyObject, vAttrs);
 +        }
 +
 +        return getCreateTaskIds(anyObject, null, null, propByRes, noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getUserCreateTasks(
 +            final Long key,
 +            final Boolean enable,
 +            final PropagationByResource propByRes,
 +            final String password,
 +            final Collection<AttrTO> vAttrs,
 +            final Collection<String> noPropResourceNames) {
 +
 +        User user = userDAO.authFind(key);
 +        if (vAttrs != null && !vAttrs.isEmpty()) {
 +            virAttrHandler.fillVirtual(user, vAttrs);
 +        }
 +        return getCreateTaskIds(user, password, enable, propByRes, noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getGroupCreateTasks(
 +            final WorkflowResult<Long> wfResult,
 +            final Collection<AttrTO> vAttrs,
 +            final Collection<String> noPropResourceNames) {
 +
 +        return getGroupCreateTasks(wfResult.getResult(), vAttrs, wfResult.getPropByRes(), noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getGroupCreateTasks(
 +            final Long key,
 +            final Collection<AttrTO> vAttrs,
 +            final PropagationByResource propByRes,
 +            final Collection<String> noPropResourceNames) {
 +
 +        Group group = groupDAO.authFind(key);
 +        if (vAttrs != null && !vAttrs.isEmpty()) {
 +            virAttrHandler.fillVirtual(group, vAttrs);
 +        }
 +
 +        return getCreateTaskIds(group, null, null, propByRes, noPropResourceNames);
 +    }
 +
 +    protected List<PropagationTask> getCreateTaskIds(final Any<?, ?, ?> any,
 +            final String password, final Boolean enable,
 +            final PropagationByResource propByRes,
 +            final Collection<String> noPropResourceNames) {
 +
 +        if (propByRes == null || propByRes.isEmpty()) {
 +            return Collections.<PropagationTask>emptyList();
 +        }
 +
 +        if (noPropResourceNames != null) {
 +            propByRes.get(ResourceOperation.CREATE).removeAll(noPropResourceNames);
 +        }
 +
 +        return createTasks(any, password, true, null, null, enable, false, propByRes);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getAnyObjectUpdateTasks(
 +            final WorkflowResult<Long> wfResult,
 +            final Set<String> vAttrsToBeRemoved, final Set<AttrMod> vAttrsToBeUpdated,
 +            final Set<String> noPropResourceNames) {
 +
 +        AnyObject anyObject = anyObjectDAO.authFind(wfResult.getResult());
 +        return getUpdateTasks(anyObject, null, false, null,
 +                vAttrsToBeRemoved, vAttrsToBeUpdated, wfResult.getPropByRes(), noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getUserUpdateTasks(final Long key, final Boolean enable,
 +            final Collection<String> noPropResourceNames) {
 +
 +        return getUpdateTasks(
 +                userDAO.find(key), // user to be updated on external resources
 +                null, // no password
 +                false,
 +                enable, // status to be propagated
 +                Collections.<String>emptySet(), // no virtual attributes to be managed
 +                Collections.<AttrMod>emptySet(), // no virtual attributes to be managed
 +                null, // no propagation by resources
 +                noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getUserUpdateTasks(final WorkflowResult<Pair<UserMod, Boolean>> wfResult,
 +            final boolean changePwd, final Collection<String> noPropResourceNames) {
 +
 +        User user = userDAO.authFind(wfResult.getResult().getKey().getKey());
 +        return getUpdateTasks(user,
 +                wfResult.getResult().getKey().getPassword(),
 +                changePwd,
 +                wfResult.getResult().getValue(),
 +                wfResult.getResult().getKey().getVirAttrsToRemove(),
 +                wfResult.getResult().getKey().getVirAttrsToUpdate(),
 +                wfResult.getPropByRes(),
 +                noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getUserUpdateTasks(final WorkflowResult<Pair<UserMod, Boolean>> wfResult) {
 +        UserMod userMod = wfResult.getResult().getKey();
 +
 +        // Propagate password update only to requested resources
 +        List<PropagationTask> tasks = new ArrayList<>();
 +        if (userMod.getPwdPropRequest() == null) {
 +            // a. no specific password propagation request: generate propagation tasks for any resource associated
 +            tasks = getUserUpdateTasks(wfResult, false, null);
 +        } else {
 +            // b. generate the propagation task list in two phases: first the ones containing password,
 +            // the the rest (with no password)
 +            PropagationByResource origPropByRes = new PropagationByResource();
 +            origPropByRes.merge(wfResult.getPropByRes());
 +
-             Set<String> pwdResourceNames = new HashSet<>(userMod.getPwdPropRequest().getResourceNames());
++            Set<String> pwdResourceNames = new HashSet<>(userMod.getPwdPropRequest().getResources());
 +            Collection<String> currentResourceNames = userDAO.findAllResourceNames(userDAO.authFind(userMod.getKey()));
 +            pwdResourceNames.retainAll(currentResourceNames);
 +            PropagationByResource pwdPropByRes = new PropagationByResource();
 +            pwdPropByRes.addAll(ResourceOperation.UPDATE, pwdResourceNames);
 +            if (!pwdPropByRes.isEmpty()) {
 +                Set<String> toBeExcluded = new HashSet<>(currentResourceNames);
 +                toBeExcluded.addAll(userMod.getResourcesToAdd());
 +                toBeExcluded.removeAll(pwdResourceNames);
 +                tasks.addAll(getUserUpdateTasks(wfResult, true, toBeExcluded));
 +            }
 +
 +            PropagationByResource nonPwdPropByRes = new PropagationByResource();
 +            nonPwdPropByRes.merge(origPropByRes);
 +            nonPwdPropByRes.removeAll(pwdResourceNames);
 +            nonPwdPropByRes.purge();
 +            if (!nonPwdPropByRes.isEmpty()) {
 +                tasks.addAll(getUserUpdateTasks(wfResult, false, pwdResourceNames));
 +            }
 +        }
 +
 +        return tasks;
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getGroupUpdateTasks(final WorkflowResult<Long> wfResult,
 +            final Set<String> vAttrsToBeRemoved, final Set<AttrMod> vAttrsToBeUpdated,
 +            final Set<String> noPropResourceNames) {
 +
 +        Group group = groupDAO.authFind(wfResult.getResult());
 +        return getUpdateTasks(group, null, false, null,
 +                vAttrsToBeRemoved, vAttrsToBeUpdated, wfResult.getPropByRes(), noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getUpdateTasks(final Any<?, ?, ?> any,
 +            final String password, final boolean changePwd, final Boolean enable,
 +            final Set<String> vAttrsToBeRemoved, final Set<AttrMod> vAttrsToBeUpdated,
 +            final PropagationByResource propByRes, final Collection<String> noPropResourceNames) {
 +
 +        PropagationByResource localPropByRes = virAttrHandler.fillVirtual(
 +                any,
 +                vAttrsToBeRemoved == null
 +                        ? Collections.<String>emptySet()
 +                        : vAttrsToBeRemoved, vAttrsToBeUpdated == null
 +                        ? Collections.<AttrMod>emptySet()
 +                        : vAttrsToBeUpdated);
 +
 +        if (propByRes == null || propByRes.isEmpty()) {
 +            localPropByRes.addAll(ResourceOperation.UPDATE, any.getResourceNames());
 +        } else {
 +            localPropByRes.merge(propByRes);
 +        }
 +
 +        if (noPropResourceNames != null) {
 +            localPropByRes.removeAll(noPropResourceNames);
 +        }
 +
 +        Map<String, AttrMod> vAttrsToBeUpdatedMap = null;
 +        if (vAttrsToBeUpdated != null) {
 +            vAttrsToBeUpdatedMap = new HashMap<>();
 +            for (AttrMod attrMod : vAttrsToBeUpdated) {
 +                vAttrsToBeUpdatedMap.put(attrMod.getSchema(), attrMod);
 +            }
 +        }
 +
 +        return createTasks(
 +                any, password, changePwd, vAttrsToBeRemoved, vAttrsToBeUpdatedMap, enable, false, localPropByRes);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getAnyObjectDeleteTasks(final Long anyObjectKey) {
 +        return getAnyObjectDeleteTasks(anyObjectKey, Collections.<String>emptySet());
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getAnyObjectDeleteTasks(final Long anyObjectKey, final String noPropResourceName) {
 +        return getAnyObjectDeleteTasks(anyObjectKey, Collections.<String>singleton(noPropResourceName));
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getAnyObjectDeleteTasks(
 +            final Long anyObjectKey, final Collection<String> noPropResourceNames) {
 +
 +        AnyObject anyObject = anyObjectDAO.authFind(anyObjectKey);
 +        return getDeleteTaskIds(anyObject, anyObject.getResourceNames(), noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getAnyObjectDeleteTasks(
 +            final Long groupKey, final Set<String> resourceNames, final Collection<String> noPropResourceNames) {
 +
 +        AnyObject anyObject = anyObjectDAO.authFind(groupKey);
 +        return getDeleteTaskIds(anyObject, resourceNames, noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getUserDeleteTasks(final Long userKey, final Collection<String> noPropResourceNames) {
 +        User user = userDAO.authFind(userKey);
 +        return getDeleteTaskIds(user, userDAO.findAllResourceNames(user), noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getUserDeleteTasks(
 +            final Long userKey, final Set<String> resourceNames, final Collection<String> noPropResourceNames) {
 +
 +        User user = userDAO.authFind(userKey);
 +        return getDeleteTaskIds(user, resourceNames, noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getGroupDeleteTasks(final Long groupKey) {
 +        return getGroupDeleteTasks(groupKey, Collections.<String>emptySet());
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getGroupDeleteTasks(final Long groupKey, final String noPropResourceName) {
 +        return getGroupDeleteTasks(groupKey, Collections.<String>singleton(noPropResourceName));
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getGroupDeleteTasks(
 +            final Long groupKey, final Collection<String> noPropResourceNames) {
 +
 +        Group group = groupDAO.authFind(groupKey);
 +        return getDeleteTaskIds(group, group.getResourceNames(), noPropResourceNames);
 +    }
 +
 +    @Override
 +    public List<PropagationTask> getGroupDeleteTasks(
 +            final Long groupKey, final Set<String> resourceNames, final Collection<String> noPropResourceNames) {
 +
 +        Group group = groupDAO.authFind(groupKey);
 +        return getDeleteTaskIds(group, resourceNames, noPropResourceNames);
 +    }
 +
 +    protected List<PropagationTask> getDeleteTaskIds(
 +            final Any<?, ?, ?> any,
 +            final Collection<String> resourceNames,
 +            final Collection<String> noPropResourceNames) {
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.set(ResourceOperation.DELETE, resourceNames);
 +        if (noPropResourceNames != null && !noPropResourceNames.isEmpty()) {
 +            propByRes.get(ResourceOperation.DELETE).removeAll(noPropResourceNames);
 +        }
 +        return createTasks(any, null, false, null, null, false, true, propByRes);
 +    }
 +
 +    /**
 +     * Create propagation tasks.
 +     *
 +     * @param any user / group to be provisioned
 +     * @param password cleartext password to be provisioned
 +     * @param changePwd whether password should be included for propagation attributes or not
 +     * @param vAttrsToBeRemoved virtual attributes to be removed
 +     * @param vAttrsToBeUpdated virtual attributes to be added
 +     * @param enable whether user must be enabled or not
 +     * @param deleteOnResource whether user / group must be deleted anyway from external resource or not
 +     * @param propByRes operation to be performed per resource
 +     * @return list of propagation tasks created
 +     */
 +    protected List<PropagationTask> createTasks(final Any<?, ?, ?> any,
 +            final String password, final boolean changePwd,
 +            final Set<String> vAttrsToBeRemoved, final Map<String, AttrMod> vAttrsToBeUpdated,
 +            final Boolean enable, final boolean deleteOnResource, final PropagationByResource propByRes) {
 +
 +        LOG.debug("Provisioning any {}:\n{}", any, propByRes);
 +
 +        if (!propByRes.get(ResourceOperation.CREATE).isEmpty()
 +                && vAttrsToBeRemoved != null && vAttrsToBeUpdated != null) {
 +
 +            virAttrHandler.retrieveVirAttrValues(any);
 +
 +            // update vAttrsToBeUpdated as well
 +            for (VirAttr<?> virAttr : any.getVirAttrs()) {
 +                String schema = virAttr.getSchema().getKey();
 +
 +                AttrMod attributeMod = new AttrMod();
 +                attributeMod.setSchema(schema);
 +                attributeMod.getValuesToBeAdded().addAll(virAttr.getValues());
 +
 +                vAttrsToBeUpdated.put(schema, attributeMod);
 +            }
 +        }
 +
 +        // Avoid duplicates - see javadoc
 +        propByRes.purge();
 +        LOG.debug("After purge: {}", propByRes);
 +
 +        List<PropagationTask> tasks = new ArrayList<>();
 +
 +        for (ResourceOperation operation : ResourceOperation.values()) {
 +            for (String resourceName : propByRes.get(operation)) {
 +                ExternalResource resource = resourceDAO.find(resourceName);
 +                Provision provision = resource == null ? null : resource.getProvision(any.getType());
 +                if (resource == null) {
 +                    LOG.error("Invalid resource name specified: {}, ignoring...", resourceName);
 +                } else if (provision == null) {
 +                    LOG.error("No provision specified on resource {} for type {}, ignoring...",
 +                            resource, any.getType());
 +                } else if (MappingUtils.getMappingItems(provision, MappingPurpose.PROPAGATION).isEmpty()) {
 +                    LOG.warn("Requesting propagation for {} but no propagation mapping provided for {}",
 +                            any.getType(), resource);
 +                } else {
 +                    PropagationTask task = entityFactory.newEntity(PropagationTask.class);
 +                    task.setResource(resource);
 +                    task.setObjectClassName(
 +                            resource.getProvision(any.getType()).getObjectClass().getObjectClassValue());
 +                    task.setAnyTypeKind(any.getType().getKind());
 +                    if (!deleteOnResource) {
 +                        task.setAnyKey(any.getKey());
 +                    }
 +                    task.setPropagationOperation(operation);
 +                    task.setPropagationMode(resource.getPropagationMode());
 +                    task.setOldConnObjectKey(propByRes.getOldConnObjectKey(resource.getKey()));
 +
 +                    Pair<String, Set<Attribute>> preparedAttrs = MappingUtils.prepareAttrs(
 +                            any, password, changePwd, vAttrsToBeRemoved, vAttrsToBeUpdated, enable, provision);
 +                    task.setConnObjectKey(preparedAttrs.getKey());
 +
 +                    // Check if any of mandatory attributes (in the mapping) is missing or not received any value: 
 +                    // if so, add special attributes that will be evaluated by PropagationTaskExecutor
 +                    List<String> mandatoryMissing = new ArrayList<>();
 +                    List<String> mandatoryNullOrEmpty = new ArrayList<>();
 +                    for (MappingItem item : MappingUtils.getMappingItems(provision, MappingPurpose.PROPAGATION)) {
 +                        if (!item.isConnObjectKey()
 +                                && JexlUtils.evaluateMandatoryCondition(item.getMandatoryCondition(), any)) {
 +
 +                            Attribute attr = AttributeUtil.find(item.getExtAttrName(), preparedAttrs.getValue());
 +                            if (attr == null) {
 +                                mandatoryMissing.add(item.getExtAttrName());
 +                            } else if (attr.getValue() == null || attr.getValue().isEmpty()) {
 +                                mandatoryNullOrEmpty.add(item.getExtAttrName());
 +                            }
 +                        }
 +                    }
 +                    if (!mandatoryMissing.isEmpty()) {
 +                        preparedAttrs.getValue().add(AttributeBuilder.build(
 +                                PropagationTaskExecutor.MANDATORY_MISSING_ATTR_NAME, mandatoryMissing));
 +                    }
 +                    if (!mandatoryNullOrEmpty.isEmpty()) {
 +                        preparedAttrs.getValue().add(AttributeBuilder.build(
 +                                PropagationTaskExecutor.MANDATORY_NULL_OR_EMPTY_ATTR_NAME, mandatoryNullOrEmpty));
 +                    }
 +
 +                    task.setAttributes(preparedAttrs.getValue());
 +                    tasks.add(task);
 +
 +                    LOG.debug("PropagationTask created: {}", task);
 +                }
 +            }
 +        }
 +
 +        return tasks;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/054ea9ca/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
----------------------------------------------------------------------
diff --cc core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
index 7a4ba38,0000000..db95d8d
mode 100644,000000..100644
--- a/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
+++ b/core/workflow-activiti/src/main/java/org/apache/syncope/core/workflow/activiti/ActivitiUserWorkflowAdapter.java
@@@ -1,857 -1,0 +1,857 @@@
 +/*
 + * 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.workflow.activiti;
 +
 +import com.fasterxml.jackson.databind.JsonNode;
 +import com.fasterxml.jackson.databind.ObjectMapper;
 +import com.fasterxml.jackson.databind.node.ObjectNode;
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.io.OutputStream;
 +import java.util.ArrayList;
 +import java.util.Collections;
 +import java.util.HashMap;
 +import java.util.HashSet;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Set;
 +import javax.annotation.Resource;
 +import javax.ws.rs.NotFoundException;
 +import org.activiti.bpmn.converter.BpmnXMLConverter;
 +import org.activiti.bpmn.model.BpmnModel;
 +import org.activiti.editor.constants.ModelDataJsonConstants;
 +import org.activiti.editor.language.json.converter.BpmnJsonConverter;
 +import org.activiti.engine.ActivitiException;
 +import org.activiti.engine.ProcessEngine;
 +import org.activiti.engine.form.FormProperty;
 +import org.activiti.engine.form.FormType;
 +import org.activiti.engine.form.TaskFormData;
 +import org.activiti.engine.history.HistoricActivityInstance;
 +import org.activiti.engine.history.HistoricDetail;
 +import org.activiti.engine.history.HistoricTaskInstance;
 +import org.activiti.engine.impl.persistence.entity.HistoricFormPropertyEntity;
 +import org.activiti.engine.query.Query;
 +import org.activiti.engine.repository.Model;
 +import org.activiti.engine.repository.ProcessDefinition;
 +import org.activiti.engine.runtime.ProcessInstance;
 +import org.activiti.engine.task.Task;
 +import org.apache.commons.io.IOUtils;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.commons.lang3.tuple.ImmutablePair;
 +import org.apache.commons.lang3.tuple.Pair;
 +import org.apache.syncope.common.lib.SyncopeClientException;
 +import org.apache.syncope.common.lib.mod.StatusMod;
 +import org.apache.syncope.common.lib.mod.UserMod;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.to.WorkflowFormPropertyTO;
 +import org.apache.syncope.common.lib.to.WorkflowFormTO;
 +import org.apache.syncope.common.lib.types.PropagationByResource;
 +import org.apache.syncope.common.lib.types.ResourceOperation;
 +import org.apache.syncope.common.lib.types.WorkflowFormPropertyType;
 +import org.apache.syncope.core.misc.security.AuthContextUtils;
 +import org.apache.syncope.core.misc.spring.BeanUtils;
 +import org.apache.syncope.core.persistence.api.attrvalue.validation.InvalidEntityException;
 +import org.apache.syncope.core.persistence.api.attrvalue.validation.ParsingValidationException;
 +import org.apache.syncope.core.persistence.api.entity.user.User;
 +import org.apache.syncope.core.provisioning.api.WorkflowResult;
 +import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
 +import org.apache.syncope.core.workflow.api.WorkflowDefinitionFormat;
 +import org.apache.syncope.core.workflow.api.WorkflowException;
 +import org.apache.syncope.core.workflow.java.AbstractUserWorkflowAdapter;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.transaction.annotation.Transactional;
 +
 +/**
 + * Activiti {@link http://www.activiti.org/} based implementation.
 + */
 +public class ActivitiUserWorkflowAdapter extends AbstractUserWorkflowAdapter {
 +
 +    protected static final String[] PROPERTY_IGNORE_PROPS = { "type" };
 +
 +    public static final String WF_PROCESS_ID = "userWorkflow";
 +
 +    public static final String WF_PROCESS_RESOURCE = "userWorkflow.bpmn20.xml";
 +
 +    public static final String WF_DGRM_RESOURCE = "userWorkflow.userWorkflow.png";
 +
 +    public static final String USER = "user";
 +
 +    public static final String WF_EXECUTOR = "wfExecutor";
 +
 +    public static final String FORM_SUBMITTER = "formSubmitter";
 +
 +    public static final String USER_TO = "userTO";
 +
 +    public static final String ENABLED = "enabled";
 +
 +    public static final String USER_MOD = "userMod";
 +
 +    public static final String EMAIL_KIND = "emailKind";
 +
 +    public static final String TASK = "task";
 +
 +    public static final String TOKEN = "token";
 +
 +    public static final String PASSWORD = "password";
 +
 +    public static final String PROP_BY_RESOURCE = "propByResource";
 +
 +    public static final String PROPAGATE_ENABLE = "propagateEnable";
 +
 +    public static final String ENCRYPTED_PWD = "encryptedPwd";
 +
 +    public static final String TASK_IS_FORM = "taskIsForm";
 +
 +    public static final String MODEL_DATA_JSON_MODEL = "model";
 +
 +    public static final String STORE_PASSWORD = "storePassword";
 +
 +    public static final String EVENT = "event";
 +
 +    @Resource(name = "adminUser")
 +    protected String adminUser;
 +
 +    @Autowired
 +    protected ProcessEngine engine;
 +
 +    @Autowired
 +    protected UserDataBinder userDataBinder;
 +
 +    @Override
 +    public String getPrefix() {
 +        return "ACT_";
 +    }
 +
 +    protected void throwException(final ActivitiException e, final String defaultMessage) {
 +        if (e.getCause() != null) {
 +            if (e.getCause().getCause() instanceof SyncopeClientException) {
 +                throw (SyncopeClientException) e.getCause().getCause();
 +            } else if (e.getCause().getCause() instanceof ParsingValidationException) {
 +                throw (ParsingValidationException) e.getCause().getCause();
 +            } else if (e.getCause().getCause() instanceof InvalidEntityException) {
 +                throw (InvalidEntityException) e.getCause().getCause();
 +            }
 +        }
 +
 +        throw new WorkflowException(defaultMessage, e);
 +    }
 +
 +    protected void updateStatus(final User user) {
 +        List<Task> tasks = engine.getTaskService().createTaskQuery().processInstanceId(user.getWorkflowId()).list();
 +        if (tasks.isEmpty() || tasks.size() > 1) {
 +            LOG.warn("While setting user status: unexpected task number ({})", tasks.size());
 +        } else {
 +            user.setStatus(tasks.get(0).getTaskDefinitionKey());
 +        }
 +    }
 +
 +    protected String getFormTask(final User user) {
 +        String result = null;
 +
 +        List<Task> tasks = engine.getTaskService().createTaskQuery().processInstanceId(user.getWorkflowId()).list();
 +        if (tasks.isEmpty() || tasks.size() > 1) {
 +            LOG.warn("While checking if form task: unexpected task number ({})", tasks.size());
 +        } else {
 +            try {
 +                TaskFormData formData = engine.getFormService().getTaskFormData(tasks.get(0).getId());
 +                if (formData != null && !formData.getFormProperties().isEmpty()) {
 +                    result = tasks.get(0).getId();
 +                }
 +            } catch (ActivitiException e) {
 +                LOG.warn("Could not get task form data", e);
 +            }
 +        }
 +
 +        return result;
 +    }
 +
 +    protected Set<String> getPerformedTasks(final User user) {
 +        final Set<String> result = new HashSet<>();
 +
 +        for (HistoricActivityInstance task : engine.getHistoryService().createHistoricActivityInstanceQuery().
 +                executionId(user.getWorkflowId()).list()) {
 +
 +            result.add(task.getActivityId());
 +        }
 +
 +        return result;
 +    }
 +
 +    /**
 +     * Saves resources to be propagated and password for later - after form submission - propagation.
 +     */
 +    protected void saveForFormSubmit(final User user, final String password, final PropagationByResource propByRes) {
 +        String formTaskId = getFormTask(user);
 +        if (formTaskId != null) {
 +            // SYNCOPE-238: This is needed to simplify the task query in this.getForms()
 +            engine.getTaskService().setVariableLocal(formTaskId, TASK_IS_FORM, Boolean.TRUE);
 +            engine.getRuntimeService().setVariable(user.getWorkflowId(), PROP_BY_RESOURCE, propByRes);
 +            if (propByRes != null) {
 +                propByRes.clear();
 +            }
 +
 +            if (StringUtils.isNotBlank(password)) {
 +                engine.getRuntimeService().setVariable(user.getWorkflowId(), ENCRYPTED_PWD, encrypt(password));
 +            }
 +        }
 +    }
 +
 +    @Override
 +    public WorkflowResult<Pair<Long, Boolean>> create(final UserTO userTO, final boolean disablePwdPolicyCheck,
 +            final boolean storePassword) {
 +
 +        return create(userTO, disablePwdPolicyCheck, null, storePassword);
 +    }
 +
 +    @Override
 +    public WorkflowResult<Pair<Long, Boolean>> create(final UserTO userTO, final boolean storePassword) {
 +        return create(userTO, false, storePassword);
 +    }
 +
 +    @Override
 +    public WorkflowResult<Pair<Long, Boolean>> create(final UserTO userTO, final boolean disablePwdPolicyCheck,
 +            final Boolean enabled, final boolean storePassword) {
 +
 +        Map<String, Object> variables = new HashMap<>();
 +        variables.put(WF_EXECUTOR, AuthContextUtils.getUsername());
 +        variables.put(USER_TO, userTO);
 +        variables.put(ENABLED, enabled);
 +        variables.put(STORE_PASSWORD, storePassword);
 +
 +        ProcessInstance processInstance = null;
 +        try {
 +            processInstance = engine.getRuntimeService().startProcessInstanceByKey(WF_PROCESS_ID, variables);
 +        } catch (ActivitiException e) {
 +            throwException(e, "While starting " + WF_PROCESS_ID + " instance");
 +        }
 +
 +        User user = engine.getRuntimeService().getVariable(processInstance.getProcessInstanceId(), USER, User.class);
 +
 +        Boolean updatedEnabled =
 +                engine.getRuntimeService().getVariable(processInstance.getProcessInstanceId(), ENABLED, Boolean.class);
 +        if (updatedEnabled != null) {
 +            user.setSuspended(!updatedEnabled);
 +        }
 +
 +        // this will make UserValidator not to consider password policies at all
 +        if (disablePwdPolicyCheck) {
 +            user.removeClearPassword();
 +        }
 +
 +        updateStatus(user);
 +        user = userDAO.save(user);
 +
 +        Boolean propagateEnable = engine.getRuntimeService().getVariable(
 +                processInstance.getProcessInstanceId(), PROPAGATE_ENABLE, Boolean.class);
 +        if (propagateEnable == null) {
 +            propagateEnable = enabled;
 +        }
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.set(ResourceOperation.CREATE, userDAO.findAllResourceNames(user));
 +
 +        saveForFormSubmit(user, userTO.getPassword(), propByRes);
 +
 +        return new WorkflowResult<Pair<Long, Boolean>>(
 +                new ImmutablePair<>(user.getKey(), propagateEnable), propByRes, getPerformedTasks(user));
 +    }
 +
 +    protected Set<String> doExecuteTask(final User user, final String task, final Map<String, Object> moreVariables) {
 +        Set<String> preTasks = getPerformedTasks(user);
 +
 +        Map<String, Object> variables = new HashMap<>();
 +        variables.put(WF_EXECUTOR, AuthContextUtils.getUsername());
 +        variables.put(TASK, task);
 +
 +        // using BeanUtils to access all user's properties and trigger lazy loading - we are about to
 +        // serialize a User instance for availability within workflow tasks, and this breaks transactions
 +        BeanUtils.copyProperties(user, entityFactory.newEntity(User.class));
 +        variables.put(USER, user);
 +
 +        if (moreVariables != null && !moreVariables.isEmpty()) {
 +            variables.putAll(moreVariables);
 +        }
 +
 +        if (StringUtils.isBlank(user.getWorkflowId())) {
 +            throw new WorkflowException(new NotFoundException("Empty workflow id for " + user));
 +        }
 +
 +        List<Task> tasks = engine.getTaskService().createTaskQuery().processInstanceId(user.getWorkflowId()).list();
 +        if (tasks.size() == 1) {
 +            try {
 +                engine.getTaskService().complete(tasks.get(0).getId(), variables);
 +            } catch (ActivitiException e) {
 +                throwException(e, "While completing task '" + tasks.get(0).getName() + "' for " + user);
 +            }
 +        } else {
 +            LOG.warn("Expected a single task, found {}", tasks.size());
 +        }
 +
 +        Set<String> postTasks = getPerformedTasks(user);
 +        postTasks.removeAll(preTasks);
 +        postTasks.add(task);
 +        return postTasks;
 +    }
 +
 +    @Override
 +    protected WorkflowResult<Long> doActivate(final User user, final String token) {
 +        Set<String> tasks = doExecuteTask(user, "activate", Collections.singletonMap(TOKEN, (Object) token));
 +
 +        updateStatus(user);
 +        User updated = userDAO.save(user);
 +
 +        return new WorkflowResult<>(updated.getKey(), null, tasks);
 +    }
 +
 +    @Override
 +    protected WorkflowResult<Pair<UserMod, Boolean>> doUpdate(final User user, final UserMod userMod) {
 +        Set<String> tasks = doExecuteTask(user, "update", Collections.singletonMap(USER_MOD, (Object) userMod));
 +
 +        updateStatus(user);
 +        User updated = userDAO.save(user);
 +
 +        PropagationByResource propByRes = engine.getRuntimeService().getVariable(
 +                user.getWorkflowId(), PROP_BY_RESOURCE, PropagationByResource.class);
 +        UserMod updatedMod = engine.getRuntimeService().getVariable(
 +                user.getWorkflowId(), USER_MOD, UserMod.class);
 +
 +        saveForFormSubmit(updated, updatedMod.getPassword(), propByRes);
 +
 +        Boolean propagateEnable = engine.getRuntimeService().getVariable(
 +                user.getWorkflowId(), PROPAGATE_ENABLE, Boolean.class);
 +
 +        return new WorkflowResult<Pair<UserMod, Boolean>>(
 +                new ImmutablePair<>(updatedMod, propagateEnable), propByRes, tasks);
 +    }
 +
 +    @Override
 +    protected WorkflowResult<Long> doSuspend(final User user) {
 +        Set<String> performedTasks = doExecuteTask(user, "suspend", null);
 +        updateStatus(user);
 +        User updated = userDAO.save(user);
 +
 +        return new WorkflowResult<>(updated.getKey(), null, performedTasks);
 +    }
 +
 +    @Override
 +    protected WorkflowResult<Long> doReactivate(final User user) {
 +        Set<String> performedTasks = doExecuteTask(user, "reactivate", null);
 +        updateStatus(user);
 +
 +        User updated = userDAO.save(user);
 +
 +        return new WorkflowResult<>(updated.getKey(), null, performedTasks);
 +    }
 +
 +    @Override
 +    protected void doRequestPasswordReset(final User user) {
 +        Map<String, Object> variables = new HashMap<>(2);
 +        variables.put(USER_TO, userDataBinder.getUserTO(user, true));
 +        variables.put(EVENT, "requestPasswordReset");
 +
 +        doExecuteTask(user, "requestPasswordReset", variables);
 +        userDAO.save(user);
 +    }
 +
 +    @Override
 +    protected void doConfirmPasswordReset(final User user, final String token, final String password) {
 +        Map<String, Object> variables = new HashMap<>(4);
 +        variables.put(TOKEN, token);
 +        variables.put(PASSWORD, password);
 +        variables.put(USER_TO, userDataBinder.getUserTO(user, true));
 +        variables.put(EVENT, "confirmPasswordReset");
 +
 +        doExecuteTask(user, "confirmPasswordReset", variables);
 +        userDAO.save(user);
 +    }
 +
 +    @Override
 +    protected void doDelete(final User user) {
 +        doExecuteTask(user, "delete", null);
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +        propByRes.set(ResourceOperation.DELETE, userDAO.findAllResourceNames(user));
 +
 +        saveForFormSubmit(user, null, propByRes);
 +
 +        if (engine.getRuntimeService().createProcessInstanceQuery().
 +                processInstanceId(user.getWorkflowId()).active().list().isEmpty()) {
 +
 +            userDAO.delete(user.getKey());
 +
 +            if (!engine.getHistoryService().createHistoricProcessInstanceQuery().
 +                    processInstanceId(user.getWorkflowId()).list().isEmpty()) {
 +
 +                engine.getHistoryService().deleteHistoricProcessInstance(user.getWorkflowId());
 +            }
 +        } else {
 +            updateStatus(user);
 +            userDAO.save(user);
 +        }
 +    }
 +
 +    @Override
 +    public WorkflowResult<Long> execute(final UserTO userTO, final String taskId) {
 +        User user = userDAO.authFind(userTO.getKey());
 +
 +        final Map<String, Object> variables = new HashMap<>();
 +        variables.put(USER_TO, userTO);
 +
 +        Set<String> performedTasks = doExecuteTask(user, taskId, variables);
 +        updateStatus(user);
 +        User updated = userDAO.save(user);
 +
 +        return new WorkflowResult<>(updated.getKey(), null, performedTasks);
 +    }
 +
 +    protected ProcessDefinition getProcessDefinition() {
 +        try {
 +            return engine.getRepositoryService().createProcessDefinitionQuery().processDefinitionKey(
 +                    ActivitiUserWorkflowAdapter.WF_PROCESS_ID).latestVersion().singleResult();
 +        } catch (ActivitiException e) {
 +            throw new WorkflowException("While accessing process " + ActivitiUserWorkflowAdapter.WF_PROCESS_ID, e);
 +        }
 +
 +    }
 +
 +    protected Model getModel(final ProcessDefinition procDef) {
 +        try {
 +            Model model = engine.getRepositoryService().createModelQuery().
 +                    deploymentId(procDef.getDeploymentId()).singleResult();
 +            if (model == null) {
 +                throw new NotFoundException("Could not find Model for deployment " + procDef.getDeploymentId());
 +            }
 +            return model;
 +        } catch (Exception e) {
 +            throw new WorkflowException("While accessing process " + ActivitiUserWorkflowAdapter.WF_PROCESS_ID, e);
 +        }
 +    }
 +
 +    protected void exportProcessResource(final String resourceName, final OutputStream os) {
 +        ProcessDefinition procDef = getProcessDefinition();
 +
 +        InputStream procDefIS = engine.getRepositoryService().getResourceAsStream(procDef.getDeploymentId(),
 +                resourceName);
 +        try {
 +            IOUtils.copy(procDefIS, os);
 +        } catch (IOException e) {
 +            LOG.error("While exporting workflow definition {}", procDef.getKey(), e);
 +        } finally {
 +            IOUtils.closeQuietly(procDefIS);
 +        }
 +    }
 +
 +    protected void exportProcessModel(final OutputStream os) {
 +        Model model = getModel(getProcessDefinition());
 +
 +        ObjectMapper objectMapper = new ObjectMapper();
 +        try {
 +            ObjectNode modelNode = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
 +            modelNode.put(ModelDataJsonConstants.MODEL_ID, model.getId());
 +            modelNode.replace(MODEL_DATA_JSON_MODEL,
 +                    objectMapper.readTree(engine.getRepositoryService().getModelEditorSource(model.getId())));
 +
 +            os.write(modelNode.toString().getBytes());
 +        } catch (IOException e) {
 +            LOG.error("While exporting workflow definition {}", model.getId(), e);
 +        }
 +    }
 +
 +    @Override
 +    public void exportDefinition(final WorkflowDefinitionFormat format, final OutputStream os) {
 +        switch (format) {
 +            case JSON:
 +                exportProcessModel(os);
 +                break;
 +
 +            case XML:
 +            default:
 +                exportProcessResource(WF_PROCESS_RESOURCE, os);
 +        }
 +    }
 +
 +    @Override
 +    public void exportDiagram(final OutputStream os) {
 +        exportProcessResource(WF_DGRM_RESOURCE, os);
 +    }
 +
 +    @Override
 +    public void importDefinition(final WorkflowDefinitionFormat format, final String definition) {
 +        Model model = getModel(getProcessDefinition());
 +        switch (format) {
 +            case JSON:
 +                JsonNode definitionNode;
 +                try {
 +                    definitionNode = new ObjectMapper().readTree(definition);
 +                    if (definitionNode.has(MODEL_DATA_JSON_MODEL)) {
 +                        definitionNode = definitionNode.get(MODEL_DATA_JSON_MODEL);
 +                    }
 +                    if (!definitionNode.has(BpmnJsonConverter.EDITOR_CHILD_SHAPES)) {
 +                        throw new IllegalArgumentException(
 +                                "Could not find JSON node " + BpmnJsonConverter.EDITOR_CHILD_SHAPES);
 +                    }
 +
 +                    BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(definitionNode);
 +                    ActivitiImportUtils.fromXML(engine, new BpmnXMLConverter().convertToXML(bpmnModel));
 +                } catch (Exception e) {
 +                    throw new WorkflowException("While updating process "
 +                            + ActivitiUserWorkflowAdapter.WF_PROCESS_RESOURCE, e);
 +                }
 +
 +                ActivitiImportUtils.fromJSON(
 +                        engine, definitionNode.toString().getBytes(), getProcessDefinition(), model);
 +                break;
 +
 +            case XML:
 +            default:
 +                ActivitiImportUtils.fromXML(engine, definition.getBytes());
 +
 +                ActivitiImportUtils.fromJSON(engine, getProcessDefinition(), model);
 +        }
 +    }
 +
 +    protected WorkflowFormPropertyType fromActivitiFormType(final FormType activitiFormType) {
 +        WorkflowFormPropertyType result = WorkflowFormPropertyType.String;
 +
 +        if ("string".equals(activitiFormType.getName())) {
 +            result = WorkflowFormPropertyType.String;
 +        }
 +        if ("long".equals(activitiFormType.getName())) {
 +            result = WorkflowFormPropertyType.Long;
 +        }
 +        if ("enum".equals(activitiFormType.getName())) {
 +            result = WorkflowFormPropertyType.Enum;
 +        }
 +        if ("date".equals(activitiFormType.getName())) {
 +            result = WorkflowFormPropertyType.Date;
 +        }
 +        if ("boolean".equals(activitiFormType.getName())) {
 +            result = WorkflowFormPropertyType.Boolean;
 +        }
 +
 +        return result;
 +    }
 +
 +    protected WorkflowFormTO getFormTO(final Task task) {
 +        return getFormTO(task, engine.getFormService().getTaskFormData(task.getId()));
 +    }
 +
 +    protected WorkflowFormTO getFormTO(final Task task, final TaskFormData fd) {
 +        final WorkflowFormTO formTO =
 +                getFormTO(task.getProcessInstanceId(), task.getId(), fd.getFormKey(), fd.getFormProperties());
 +
 +        BeanUtils.copyProperties(task, formTO);
 +        return formTO;
 +    }
 +
 +    protected WorkflowFormTO getFormTO(final HistoricTaskInstance task) {
 +        final List<HistoricFormPropertyEntity> props = new ArrayList<>();
 +
 +        for (HistoricDetail historicDetail
 +                : engine.getHistoryService().createHistoricDetailQuery().taskId(task.getId()).list()) {
 +
 +            if (historicDetail instanceof HistoricFormPropertyEntity) {
 +                props.add((HistoricFormPropertyEntity) historicDetail);
 +            }
 +        }
 +
 +        final WorkflowFormTO formTO = getHistoricFormTO(
 +                task.getProcessInstanceId(), task.getId(), task.getFormKey(), props);
 +        BeanUtils.copyProperties(task, formTO);
 +
 +        final HistoricActivityInstance historicActivityInstance = engine.getHistoryService().
 +                createHistoricActivityInstanceQuery().
 +                executionId(task.getExecutionId()).activityType("userTask").activityName(task.getName()).singleResult();
 +
 +        if (historicActivityInstance != null) {
 +            formTO.setCreateTime(historicActivityInstance.getStartTime());
 +            formTO.setDueDate(historicActivityInstance.getEndTime());
 +        }
 +
 +        return formTO;
 +    }
 +
 +    protected WorkflowFormTO getHistoricFormTO(
 +            final String processInstanceId,
 +            final String taskId,
 +            final String formKey,
 +            final List<HistoricFormPropertyEntity> props) {
 +
 +        WorkflowFormTO formTO = new WorkflowFormTO();
 +
 +        User user = userDAO.findByWorkflowId(processInstanceId);
 +        if (user == null) {
 +            throw new NotFoundException("User with workflow id " + processInstanceId);
 +        }
 +        formTO.setUserKey(user.getKey());
 +
 +        formTO.setTaskId(taskId);
 +        formTO.setKey(formKey);
 +
 +        for (HistoricFormPropertyEntity prop : props) {
 +            WorkflowFormPropertyTO propertyTO = new WorkflowFormPropertyTO();
 +            propertyTO.setId(prop.getPropertyId());
 +            propertyTO.setName(prop.getPropertyId());
 +            propertyTO.setValue(prop.getPropertyValue());
 +            formTO.addProperty(propertyTO);
 +        }
 +
 +        return formTO;
 +    }
 +
 +    @SuppressWarnings("unchecked")
 +    protected WorkflowFormTO getFormTO(
 +            final String processInstanceId,
 +            final String taskId,
 +            final String formKey,
 +            final List<FormProperty> properties) {
 +
 +        WorkflowFormTO formTO = new WorkflowFormTO();
 +
 +        User user = userDAO.findByWorkflowId(processInstanceId);
 +        if (user == null) {
 +            throw new NotFoundException("User with workflow id " + processInstanceId);
 +        }
 +        formTO.setUserKey(user.getKey());
 +
 +        formTO.setTaskId(taskId);
 +        formTO.setKey(formKey);
 +
 +        for (FormProperty fProp : properties) {
 +            WorkflowFormPropertyTO propertyTO = new WorkflowFormPropertyTO();
 +            BeanUtils.copyProperties(fProp, propertyTO, PROPERTY_IGNORE_PROPS);
 +            propertyTO.setType(fromActivitiFormType(fProp.getType()));
 +
 +            if (propertyTO.getType() == WorkflowFormPropertyType.Date) {
 +                propertyTO.setDatePattern((String) fProp.getType().getInformation("datePattern"));
 +            }
 +            if (propertyTO.getType() == WorkflowFormPropertyType.Enum) {
 +                propertyTO.getEnumValues().putAll((Map<String, String>) fProp.getType().getInformation("values"));
 +            }
 +
 +            formTO.addProperty(propertyTO);
 +        }
 +
 +        return formTO;
 +    }
 +
 +    @Transactional(readOnly = true)
 +    @Override
 +    public List<WorkflowFormTO> getForms() {
 +        List<WorkflowFormTO> forms = new ArrayList<>();
 +
 +        String authUser = AuthContextUtils.getUsername();
 +        if (adminUser.equals(authUser)) {
 +            forms.addAll(getForms(engine.getTaskService().createTaskQuery().
 +                    taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE)));
 +        } else {
 +            User user = userDAO.find(authUser);
 +            if (user == null) {
 +                throw new NotFoundException("Syncope User " + authUser);
 +            }
 +
 +            forms.addAll(getForms(engine.getTaskService().createTaskQuery().
 +                    taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE).
 +                    taskCandidateOrAssigned(user.getKey().toString())));
 +
 +            List<String> candidateGroups = new ArrayList<>();
 +            for (Long groupId : userDAO.findAllGroupKeys(user)) {
 +                candidateGroups.add(groupId.toString());
 +            }
 +            if (!candidateGroups.isEmpty()) {
 +                forms.addAll(getForms(engine.getTaskService().createTaskQuery().
 +                        taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE).
 +                        taskCandidateGroupIn(candidateGroups)));
 +            }
 +        }
 +
 +        return forms;
 +    }
 +
 +    @Override
 +    public List<WorkflowFormTO> getForms(final String workflowId, final String name) {
 +        List<WorkflowFormTO> forms = getForms(
 +                engine.getTaskService().createTaskQuery().processInstanceId(workflowId).taskName(name).
 +                taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE));
 +
 +        forms.addAll(getForms(engine.getHistoryService().createHistoricTaskInstanceQuery().taskName(name).
 +                taskVariableValueEquals(TASK_IS_FORM, Boolean.TRUE)));
 +
 +        return forms;
 +    }
 +
 +    protected <T extends Query<?, ?>, U extends Object> List<WorkflowFormTO> getForms(final Query<T, U> query) {
 +        List<WorkflowFormTO> forms = new ArrayList<>();
 +
 +        for (U obj : query.list()) {
 +            try {
 +                if (obj instanceof HistoricTaskInstance) {
 +                    forms.add(getFormTO((HistoricTaskInstance) obj));
 +                } else if (obj instanceof Task) {
 +                    forms.add(getFormTO((Task) obj));
 +                } else {
 +                    throw new ActivitiException(
 +                            "Failure retrieving form", new IllegalArgumentException("Invalid task type"));
 +                }
 +            } catch (ActivitiException e) {
 +                LOG.debug("No form found for task {}", obj, e);
 +            }
 +        }
 +
 +        return forms;
 +    }
 +
 +    @Override
 +    public WorkflowFormTO getForm(final String workflowId) {
 +        Task task;
 +        try {
 +            task = engine.getTaskService().createTaskQuery().processInstanceId(workflowId).singleResult();
 +        } catch (ActivitiException e) {
 +            throw new WorkflowException("While reading form for workflow instance " + workflowId, e);
 +        }
 +
 +        TaskFormData formData;
 +        try {
 +            formData = engine.getFormService().getTaskFormData(task.getId());
 +        } catch (ActivitiException e) {
 +            LOG.debug("No form found for task {}", task.getId(), e);
 +            formData = null;
 +        }
 +
 +        WorkflowFormTO result = null;
 +        if (formData != null && !formData.getFormProperties().isEmpty()) {
 +            result = getFormTO(task);
 +        }
 +
 +        return result;
 +    }
 +
 +    protected Pair<Task, TaskFormData> checkTask(final String taskId, final String authUser) {
 +        Task task;
 +        try {
 +            task = engine.getTaskService().createTaskQuery().taskId(taskId).singleResult();
 +        } catch (ActivitiException e) {
 +            throw new NotFoundException("Activiti Task " + taskId, e);
 +        }
 +
 +        TaskFormData formData;
 +        try {
 +            formData = engine.getFormService().getTaskFormData(task.getId());
 +        } catch (ActivitiException e) {
 +            throw new NotFoundException("Form for Activiti Task " + taskId, e);
 +        }
 +
 +        if (!adminUser.equals(authUser)) {
 +            User user = userDAO.find(authUser);
 +            if (user == null) {
 +                throw new NotFoundException("Syncope User " + authUser);
 +            }
 +        }
 +
 +        return new ImmutablePair<>(task, formData);
 +    }
 +
 +    @Override
 +    public WorkflowFormTO claimForm(final String taskId) {
 +        String authUser = AuthContextUtils.getUsername();
 +        Pair<Task, TaskFormData> checked = checkTask(taskId, authUser);
 +
 +        if (!adminUser.equals(authUser)) {
 +            List<Task> tasksForUser = engine.getTaskService().createTaskQuery().taskId(taskId).taskCandidateUser(
 +                    authUser).list();
 +            if (tasksForUser.isEmpty()) {
 +                throw new WorkflowException(
 +                        new IllegalArgumentException(authUser + " is not candidate for task " + taskId));
 +            }
 +        }
 +
 +        Task task;
 +        try {
 +            engine.getTaskService().setOwner(taskId, authUser);
 +            task = engine.getTaskService().createTaskQuery().taskId(taskId).singleResult();
 +        } catch (ActivitiException e) {
 +            throw new WorkflowException("While reading task " + taskId, e);
 +        }
 +
 +        return getFormTO(task, checked.getValue());
 +    }
 +
 +    @Override
 +    public WorkflowResult<UserMod> submitForm(final WorkflowFormTO form) {
 +        String authUser = AuthContextUtils.getUsername();
 +        Pair<Task, TaskFormData> checked = checkTask(form.getTaskId(), authUser);
 +
 +        if (!checked.getKey().getOwner().equals(authUser)) {
 +            throw new WorkflowException(new IllegalArgumentException("Task " + form.getTaskId() + " assigned to "
 +                    + checked.getKey().getOwner() + " but submitted by " + authUser));
 +        }
 +
 +        User user = userDAO.findByWorkflowId(checked.getKey().getProcessInstanceId());
 +        if (user == null) {
 +            throw new NotFoundException("User with workflow id " + checked.getKey().getProcessInstanceId());
 +        }
 +
 +        Set<String> preTasks = getPerformedTasks(user);
 +        try {
 +            engine.getFormService().submitTaskFormData(form.getTaskId(), form.getPropertiesForSubmit());
 +            engine.getRuntimeService().setVariable(user.getWorkflowId(), FORM_SUBMITTER, authUser);
 +        } catch (ActivitiException e) {
 +            throwException(e, "While submitting form for task " + form.getTaskId());
 +        }
 +
 +        Set<String> postTasks = getPerformedTasks(user);
 +        postTasks.removeAll(preTasks);
 +        postTasks.add(form.getTaskId());
 +
 +        updateStatus(user);
 +        User updated = userDAO.save(user);
 +
 +        // see if there is any propagation to be done
 +        PropagationByResource propByRes = engine.getRuntimeService().getVariable(
 +                user.getWorkflowId(), PROP_BY_RESOURCE, PropagationByResource.class);
 +
 +        // fetch - if available - the encrypted password
 +        String clearPassword = null;
 +        String encryptedPwd = engine.getRuntimeService().getVariable(user.getWorkflowId(), ENCRYPTED_PWD, String.class);
 +        if (StringUtils.isNotBlank(encryptedPwd)) {
 +            clearPassword = decrypt(encryptedPwd);
 +        }
 +
 +        // supports approval chains
 +        saveForFormSubmit(user, clearPassword, propByRes);
 +
 +        UserMod userMod = engine.getRuntimeService().getVariable(user.getWorkflowId(), USER_MOD, UserMod.class);
 +        if (userMod == null) {
 +            userMod = new UserMod();
 +            userMod.setKey(updated.getKey());
 +            userMod.setPassword(clearPassword);
 +
 +            if (propByRes != null) {
-                 final StatusMod st = new StatusMod();
++                StatusMod st = new StatusMod();
 +                userMod.setPwdPropRequest(st);
 +                st.setOnSyncope(true);
 +                for (String res : propByRes.get(ResourceOperation.CREATE)) {
-                     st.getResourceNames().add(res);
++                    st.getResources().add(res);
 +                }
 +            }
 +        }
 +
 +        return new WorkflowResult<>(userMod, propByRes, postTasks);
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/054ea9ca/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserProvisionProcessor.java
----------------------------------------------------------------------
diff --cc ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserProvisionProcessor.java
index f85c373,0000000..b77b61f
mode 100644,000000..100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserProvisionProcessor.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserProvisionProcessor.java
@@@ -1,98 -1,0 +1,98 @@@
 +/*
 + * 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.provisioning.camel.processor;
 +
 +import java.util.List;
 +import org.apache.camel.Exchange;
 +import org.apache.camel.Processor;
 +import org.apache.commons.lang3.tuple.ImmutablePair;
 +import org.apache.commons.lang3.tuple.Pair;
 +import org.apache.syncope.common.lib.mod.StatusMod;
 +import org.apache.syncope.common.lib.mod.UserMod;
 +import org.apache.syncope.common.lib.types.PropagationByResource;
 +import org.apache.syncope.common.lib.types.ResourceOperation;
 +import org.apache.syncope.core.misc.spring.ApplicationContextProvider;
 +import org.apache.syncope.core.persistence.api.dao.UserDAO;
 +import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 +import org.apache.syncope.core.provisioning.api.WorkflowResult;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationException;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.stereotype.Component;
 +
 +@Component
 +public class UserProvisionProcessor implements Processor {
 +
 +    private static final Logger LOG = LoggerFactory.getLogger(UserProvisionProcessor.class);
 +
 +    @Autowired
 +    protected PropagationManager propagationManager;
 +
 +    @Autowired
 +    protected PropagationTaskExecutor taskExecutor;
 +
 +    @Autowired
 +    protected UserDAO userDAO;
 +
 +    @Override
 +    public void process(final Exchange exchange) {
 +        Long key = exchange.getIn().getBody(Long.class);
 +        Boolean changePwd = exchange.getProperty("changePwd", Boolean.class);
 +        String password = exchange.getProperty("password", String.class);
 +        @SuppressWarnings("unchecked")
 +        List<String> resources = exchange.getProperty("resources", List.class);
 +
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(key);
 +        userMod.getResourcesToAdd().addAll(resources);
 +
 +        if (changePwd) {
 +            StatusMod statusMod = new StatusMod();
 +            statusMod.setOnSyncope(false);
-             statusMod.getResourceNames().addAll(resources);
++            statusMod.getResources().addAll(resources);
 +            userMod.setPwdPropRequest(statusMod);
 +            userMod.setPassword(password);
 +        }
 +
 +        PropagationByResource propByRes = new PropagationByResource();
 +        for (String resource : resources) {
 +            propByRes.add(ResourceOperation.UPDATE, resource);
 +        }
 +
 +        WorkflowResult<Pair<UserMod, Boolean>> wfResult = new WorkflowResult<Pair<UserMod, Boolean>>(
 +                ImmutablePair.of(userMod, (Boolean) null), propByRes, "update");
 +
 +        List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(wfResult, changePwd, null);
 +        PropagationReporter propagationReporter =
 +                ApplicationContextProvider.getBeanFactory().getBean(PropagationReporter.class);
 +        try {
 +            taskExecutor.execute(tasks, propagationReporter);
 +        } catch (PropagationException e) {
 +            LOG.error("Error propagation primary resource", e);
 +            propagationReporter.onPrimaryResourceFailure(tasks);
 +        }
 +
 +        exchange.getOut().setBody(propagationReporter.getStatuses());
 +    }
 +
 +}


[24/33] syncope git commit: [SYNCOPE-686] Enriching UserMod's pwdPropRequest when using AES and adding resources to users via roles

Posted by md...@apache.org.
[SYNCOPE-686] Enriching UserMod's pwdPropRequest when using AES and adding resources to users via roles


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

Branch: refs/heads/SYNCOPE-156
Commit: a1737d35ad33c426e89a48259f0655ba3736ac6c
Parents: 4ae3e2c
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Wed Aug 26 16:26:23 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Wed Aug 26 16:26:23 2015 +0200

----------------------------------------------------------------------
 .../core/rest/controller/UserController.java    |   3 +-
 .../syncope/core/rest/data/UserDataBinder.java  |  15 +++
 .../syncope/core/rest/RoleTestITCase.java       |   2 +-
 .../syncope/core/rest/UserTestITCase.java       | 107 ++++++++++++++-----
 4 files changed, 97 insertions(+), 30 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/a1737d35/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java b/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java
index 5cbd5de..68843a7 100644
--- a/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java
+++ b/core/src/main/java/org/apache/syncope/core/rest/controller/UserController.java
@@ -272,7 +272,8 @@ public class UserController extends AbstractSubjectController<UserTO, UserMod> {
                 removeMemberships = true;
             }
         }
-        //Actual operations: workflow, propagation, notification
+
+        // Actual operations: workflow, propagation, notification
         WorkflowResult<Map.Entry<UserMod, Boolean>> updated = uwfAdapter.update(actual);
 
         List<PropagationTask> tasks = propagationManager.getUserUpdateTaskIds(updated);

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1737d35/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java b/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java
index 3ac56fc..fcd67c2 100644
--- a/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java
+++ b/core/src/main/java/org/apache/syncope/core/rest/data/UserDataBinder.java
@@ -30,6 +30,7 @@ import org.apache.syncope.common.SyncopeClientCompositeException;
 import org.apache.syncope.common.SyncopeClientException;
 import org.apache.syncope.common.mod.AttributeMod;
 import org.apache.syncope.common.mod.MembershipMod;
+import org.apache.syncope.common.mod.StatusMod;
 import org.apache.syncope.common.mod.UserMod;
 import org.apache.syncope.common.to.MembershipTO;
 import org.apache.syncope.common.to.UserTO;
@@ -379,6 +380,20 @@ public class UserDataBinder extends AbstractAttributableDataBinder {
                     user.addMembership(membership);
 
                     toBeProvisioned.addAll(role.getResourceNames());
+
+                    // SYNCOPE-686: if password is invertible and we are adding resources with password mapping,
+                    // ensure that they are counted for password propagation
+                    if (toBeUpdated.canDecodePassword()) {
+                        for (ExternalResource resource : role.getResources()) {
+                            if (resource.getUmapping().getPasswordItem() != null) {
+                                if (userMod.getPwdPropRequest() == null) {
+                                    userMod.setPwdPropRequest(new StatusMod());
+                                }
+
+                                userMod.getPwdPropRequest().getResourceNames().add(resource.getName());
+                            }
+                        }
+                    }
                 }
 
                 propByRes.merge(fill(membership, membershipMod,

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1737d35/core/src/test/java/org/apache/syncope/core/rest/RoleTestITCase.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/syncope/core/rest/RoleTestITCase.java b/core/src/test/java/org/apache/syncope/core/rest/RoleTestITCase.java
index 6e384d4..5f0feb0 100644
--- a/core/src/test/java/org/apache/syncope/core/rest/RoleTestITCase.java
+++ b/core/src/test/java/org/apache/syncope/core/rest/RoleTestITCase.java
@@ -71,7 +71,7 @@ import org.junit.runners.MethodSorters;
 @FixMethodOrder(MethodSorters.JVM)
 public class RoleTestITCase extends AbstractTest {
 
-    private RoleTO buildBasicRoleTO(final String name) {
+    public static RoleTO buildBasicRoleTO(final String name) {
         RoleTO roleTO = new RoleTO();
         roleTO.setName(name + getUUIDString());
         roleTO.setParent(8L);

http://git-wip-us.apache.org/repos/asf/syncope/blob/a1737d35/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java b/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
index 7d5493c..269f0a8 100644
--- a/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
+++ b/core/src/test/java/org/apache/syncope/core/rest/UserTestITCase.java
@@ -1552,38 +1552,40 @@ public class UserTestITCase extends AbstractTest {
         pwdCipherAlgo.getValues().set(0, "AES");
         configurationService.set(pwdCipherAlgo.getSchema(), pwdCipherAlgo);
 
-        // 3. create user with no resources
-        UserTO userTO = getUniqueSampleTO("syncope136_AES@apache.org");
-        userTO.getResources().clear();
-
-        userTO = createUser(userTO);
-        assertNotNull(userTO);
-
-        // 4. update user, assign a propagation primary resource but don't provide any password
-        UserMod userMod = new UserMod();
-        userMod.setId(userTO.getId());
-        userMod.getResourcesToAdd().add(RESOURCE_NAME_WS1);
+        try {
+            // 3. create user with no resources
+            UserTO userTO = getUniqueSampleTO("syncope136_AES@apache.org");
+            userTO.getResources().clear();
 
-        final StatusMod st = new StatusMod();
-        st.setOnSyncope(false);
-        st.getResourceNames().add(RESOURCE_NAME_WS1);
-        userMod.setPwdPropRequest(st);
+            userTO = createUser(userTO);
+            assertNotNull(userTO);
 
-        userTO = updateUser(userMod);
-        assertNotNull(userTO);
+            // 4. update user, assign a propagation primary resource but don't provide any password
+            UserMod userMod = new UserMod();
+            userMod.setId(userTO.getId());
+            userMod.getResourcesToAdd().add(RESOURCE_NAME_WS1);
 
-        // 5. verify that propagation was successful
-        List<PropagationStatus> props = userTO.getPropagationStatusTOs();
-        assertNotNull(props);
-        assertEquals(1, props.size());
-        PropagationStatus prop = props.iterator().next();
-        assertNotNull(prop);
-        assertEquals(RESOURCE_NAME_WS1, prop.getResource());
-        assertEquals(PropagationTaskExecStatus.SUBMITTED, prop.getStatus());
+            final StatusMod st = new StatusMod();
+            st.setOnSyncope(false);
+            st.getResourceNames().add(RESOURCE_NAME_WS1);
+            userMod.setPwdPropRequest(st);
 
-        // 6. restore initial cipher algorithm
-        pwdCipherAlgo.getValues().set(0, origpwdCipherAlgo);
-        configurationService.set(pwdCipherAlgo.getSchema(), pwdCipherAlgo);
+            userTO = updateUser(userMod);
+            assertNotNull(userTO);
+
+            // 5. verify that propagation was successful
+            List<PropagationStatus> props = userTO.getPropagationStatusTOs();
+            assertNotNull(props);
+            assertEquals(1, props.size());
+            PropagationStatus prop = props.iterator().next();
+            assertNotNull(prop);
+            assertEquals(RESOURCE_NAME_WS1, prop.getResource());
+            assertEquals(PropagationTaskExecStatus.SUBMITTED, prop.getStatus());
+        } finally {
+            // restore initial cipher algorithm
+            pwdCipherAlgo.getValues().set(0, origpwdCipherAlgo);
+            configurationService.set(pwdCipherAlgo.getSchema(), pwdCipherAlgo);
+        }
     }
 
     @Test
@@ -2533,4 +2535,53 @@ public class UserTestITCase extends AbstractTest {
         assertNotNull(connObjectTO);
         assertEquals("newPostalAddress", connObjectTO.getAttrMap().get("postalAddress").getValues().get(0));
     }
+
+    @Test
+    public void issueSYNCOPE686() {
+        // 1. read configured cipher algorithm in order to be able to restore it at the end of test
+        AttributeTO pwdCipherAlgo = configurationService.read("password.cipher.algorithm");
+        String origpwdCipherAlgo = pwdCipherAlgo.getValues().get(0);
+
+        // 2. set AES password cipher algorithm
+        pwdCipherAlgo.getValues().set(0, "AES");
+        configurationService.set(pwdCipherAlgo.getSchema(), pwdCipherAlgo);
+
+        try {
+            // 3. create role with LDAP resource assigned
+            RoleTO role = RoleTestITCase.buildBasicRoleTO("syncope686");
+            role.getResources().add(RESOURCE_NAME_LDAP);
+            role = createRole(role);
+            assertNotNull(role);
+
+            // 4. create user with no resources
+            UserTO userTO = getUniqueSampleTO("syncope686@apache.org");
+            userTO.getResources().clear();
+
+            userTO = createUser(userTO);
+            assertNotNull(userTO);
+
+            // 5. update user with the new role, and don't provide any password
+            UserMod userMod = new UserMod();
+            userMod.setId(userTO.getId());
+            MembershipMod membMod = new MembershipMod();
+            membMod.setRole(role.getId());
+            userMod.getMembershipsToAdd().add(membMod);
+
+            userTO = updateUser(userMod);
+            assertNotNull(userTO);
+
+            // 5. verify that propagation was successful
+            List<PropagationStatus> props = userTO.getPropagationStatusTOs();
+            assertNotNull(props);
+            assertEquals(1, props.size());
+            PropagationStatus prop = props.iterator().next();
+            assertNotNull(prop);
+            assertEquals(RESOURCE_NAME_LDAP, prop.getResource());
+            assertEquals(PropagationTaskExecStatus.SUCCESS, prop.getStatus());
+        } finally {
+            // restore initial cipher algorithm
+            pwdCipherAlgo.getValues().set(0, origpwdCipherAlgo);
+            configurationService.set(pwdCipherAlgo.getSchema(), pwdCipherAlgo);
+        }
+    }
 }


[10/33] syncope git commit: Merge branch '1_2_X'

Posted by md...@apache.org.
Merge branch '1_2_X'


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

Branch: refs/heads/SYNCOPE-156
Commit: 4cdb34b3c9719541dc6b19d12d7a7d8015c7d96e
Parents: 0e5694a 4ae3e2c
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Thu Aug 20 11:40:15 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Thu Aug 20 11:40:15 2015 +0200

----------------------------------------------------------------------

----------------------------------------------------------------------



[26/33] syncope git commit: [SYNCOPE-686] Merge from 1_2_X

Posted by md...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/054ea9ca/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/UserITCase.java
----------------------------------------------------------------------
diff --cc fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/UserITCase.java
index 5fe876f,0000000..375f0d7
mode 100644,000000..100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/UserITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/UserITCase.java
@@@ -1,2527 -1,0 +1,2576 @@@
 +/*
 + * 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.fit.core.reference;
 +
 +import static org.junit.Assert.assertEquals;
 +import static org.junit.Assert.assertFalse;
 +import static org.junit.Assert.assertNotEquals;
 +import static org.junit.Assert.assertNotNull;
 +import static org.junit.Assert.assertNull;
 +import static org.junit.Assert.assertTrue;
 +import static org.junit.Assert.fail;
 +
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.security.AccessControlException;
 +import java.text.SimpleDateFormat;
 +import java.util.Collection;
 +import java.util.Collections;
 +import java.util.Date;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Set;
 +import javax.naming.NamingException;
 +import javax.ws.rs.core.EntityTag;
 +import javax.ws.rs.core.MediaType;
 +import javax.ws.rs.core.Response;
 +import org.apache.commons.lang3.StringUtils;
 +import org.apache.commons.lang3.tuple.Pair;
 +import org.apache.cxf.common.util.Base64Utility;
 +import org.apache.cxf.helpers.IOUtils;
 +import org.apache.syncope.client.lib.SyncopeClient;
 +import org.apache.syncope.common.lib.AnyOperations;
 +import org.apache.syncope.common.lib.SyncopeClientException;
 +import org.apache.syncope.common.lib.SyncopeConstants;
 +import org.apache.syncope.common.lib.mod.ResourceAssociationMod;
 +import org.apache.syncope.common.lib.mod.StatusMod;
 +import org.apache.syncope.common.lib.mod.UserMod;
 +import org.apache.syncope.common.lib.policy.AccountPolicyTO;
 +import org.apache.syncope.common.lib.policy.DefaultPasswordRuleConf;
 +import org.apache.syncope.common.lib.policy.PasswordPolicyTO;
 +import org.apache.syncope.common.lib.to.AttrTO;
 +import org.apache.syncope.common.lib.to.BulkAction;
 +import org.apache.syncope.common.lib.to.BulkActionResult;
 +import org.apache.syncope.common.lib.to.BulkActionResult.Status;
 +import org.apache.syncope.common.lib.to.ConnObjectTO;
 +import org.apache.syncope.common.lib.to.MappingItemTO;
 +import org.apache.syncope.common.lib.to.MappingTO;
 +import org.apache.syncope.common.lib.to.MembershipTO;
 +import org.apache.syncope.common.lib.to.PagedResult;
 +import org.apache.syncope.common.lib.to.PropagationStatus;
 +import org.apache.syncope.common.lib.to.PropagationTaskTO;
 +import org.apache.syncope.common.lib.to.ResourceTO;
 +import org.apache.syncope.common.lib.to.GroupTO;
 +import org.apache.syncope.common.lib.to.RealmTO;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.common.lib.types.CipherAlgorithm;
 +import org.apache.syncope.common.lib.types.ClientExceptionType;
 +import org.apache.syncope.common.lib.types.MappingPurpose;
 +import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
 +import org.apache.syncope.common.lib.types.ResourceAssociationAction;
 +import org.apache.syncope.common.lib.types.ResourceDeassociationActionType;
 +import org.apache.syncope.common.lib.types.TaskType;
 +import org.apache.syncope.common.lib.wrap.ResourceKey;
 +import org.apache.syncope.common.rest.api.CollectionWrapper;
 +import org.apache.syncope.common.rest.api.Preference;
 +import org.apache.syncope.common.rest.api.RESTHeaders;
 +import org.apache.syncope.common.rest.api.service.ResourceService;
 +import org.apache.syncope.common.rest.api.service.UserSelfService;
 +import org.apache.syncope.common.rest.api.service.UserService;
 +import org.apache.syncope.core.misc.security.Encryptor;
 +import org.apache.syncope.core.provisioning.java.propagation.DBPasswordPropagationActions;
 +import org.apache.syncope.core.provisioning.java.propagation.LDAPPasswordPropagationActions;
 +import org.identityconnectors.framework.common.objects.Name;
 +import org.identityconnectors.framework.common.objects.OperationalAttributes;
 +import org.junit.Assume;
 +import org.junit.FixMethodOrder;
 +import org.junit.Test;
 +import org.junit.runners.MethodSorters;
 +import org.springframework.dao.EmptyResultDataAccessException;
 +import org.springframework.jdbc.core.JdbcTemplate;
 +
 +@FixMethodOrder(MethodSorters.JVM)
 +public class UserITCase extends AbstractITCase {
 +
 +    private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT = new ThreadLocal<SimpleDateFormat>() {
 +
 +        @Override
 +        protected SimpleDateFormat initialValue() {
 +            SimpleDateFormat sdf = new SimpleDateFormat();
 +            sdf.applyPattern("yyyy-MM-dd");
 +            return sdf;
 +        }
 +    };
 +
 +    private boolean getBooleanAttribute(final ConnObjectTO connObjectTO, final String attrName) {
 +        return Boolean.parseBoolean(connObjectTO.getPlainAttrMap().get(attrName).getValues().get(0));
 +    }
 +
 +    public static UserTO getUniqueSampleTO(final String email) {
 +        return getSampleTO(getUUIDString() + email);
 +    }
 +
 +    public static UserTO getSampleTO(final String email) {
 +        UserTO userTO = new UserTO();
 +        userTO.setRealm(SyncopeConstants.ROOT_REALM);
 +        userTO.setPassword("password123");
 +        userTO.setUsername(email);
 +
 +        userTO.getPlainAttrs().add(attrTO("fullname", email));
 +        userTO.getPlainAttrs().add(attrTO("firstname", email));
 +        userTO.getPlainAttrs().add(attrTO("surname", "surname"));
 +        userTO.getPlainAttrs().add(attrTO("type", "a type"));
 +        userTO.getPlainAttrs().add(attrTO("userId", email));
 +        userTO.getPlainAttrs().add(attrTO("email", email));
 +        userTO.getPlainAttrs().add(attrTO("loginDate", DATE_FORMAT.get().format(new Date())));
 +        userTO.getDerAttrs().add(attrTO("cn", null));
 +        userTO.getVirAttrs().add(attrTO("virtualdata", "virtualvalue"));
 +        return userTO;
 +    }
 +
 +    @Test
 +    public void createUserWithNoPropagation() {
 +        // get task list
 +        PagedResult<PropagationTaskTO> tasks = taskService.list(
 +                TaskType.PROPAGATION,
 +                SyncopeClient.getListQueryBuilder().page(1).size(1).build());
 +        assertNotNull(tasks);
 +        assertFalse(tasks.getResult().isEmpty());
 +
 +        long maxKey = tasks.getResult().iterator().next().getKey();
 +
 +        // create a new user
 +        UserTO userTO = getUniqueSampleTO("xxx@xxx.xxx");
 +
 +        userTO.setPassword("password123");
 +        userTO.getResources().add(RESOURCE_NAME_NOPROPAGATION);
 +
 +        createUser(userTO);
 +
 +        // get the new task list
 +        tasks = taskService.list(
 +                TaskType.PROPAGATION,
 +                SyncopeClient.getListQueryBuilder().page(1).size(1).build());
 +        assertNotNull(tasks);
 +        assertFalse(tasks.getResult().isEmpty());
 +
 +        long newMaxId = tasks.getResult().iterator().next().getKey();
 +
 +        assertTrue(newMaxId > maxKey);
 +
 +        // get last task
 +        PropagationTaskTO taskTO = taskService.read(newMaxId);
 +
 +        assertNotNull(taskTO);
 +        assertTrue(taskTO.getExecutions().isEmpty());
 +    }
 +
 +    @Test
 +    public void issue186() {
 +        // 1. create an user with strict mandatory attributes only
 +        UserTO userTO = new UserTO();
 +        userTO.setRealm(SyncopeConstants.ROOT_REALM);
 +        String userId = getUUIDString() + "issue186@syncope.apache.org";
 +        userTO.setUsername(userId);
 +        userTO.setPassword("password123");
 +
 +        userTO.getPlainAttrs().add(attrTO("userId", userId));
 +        userTO.getPlainAttrs().add(attrTO("fullname", userId));
 +        userTO.getPlainAttrs().add(attrTO("surname", userId));
 +
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +        assertTrue(userTO.getResources().isEmpty());
 +
 +        // 2. update assigning a resource forcing mandatory constraints: must fail with RequiredValuesMissing
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +        userMod.setPassword("newPassword123");
 +        userMod.getResourcesToAdd().add(RESOURCE_NAME_WS2);
 +
 +        try {
 +            userTO = updateUser(userMod);
 +            fail();
 +        } catch (SyncopeClientException e) {
 +            assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
 +        }
 +
 +        // 3. update assigning a resource NOT forcing mandatory constraints
 +        // AND primary: must fail with PropagationException
 +        userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +        userMod.setPassword("newPassword123");
 +        userMod.getResourcesToAdd().add(RESOURCE_NAME_WS1);
 +
 +        userTO = updateUser(userMod);
 +        assertNotNull(userTO.getPropagationStatusTOs().get(0).getFailureReason());
 +
 +        // 4. update assigning a resource NOT forcing mandatory constraints
 +        // BUT not primary: must succeed
 +        userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +        userMod.setPassword("newPassword123456");
 +        userMod.getResourcesToAdd().add(RESOURCE_NAME_CSV);
 +        updateUser(userMod);
 +    }
 +
 +    @Test
 +    public void enforceMandatoryCondition() {
 +        UserTO userTO = getUniqueSampleTO("enforce@apache.org");
 +        userTO.getResources().add(RESOURCE_NAME_WS2);
 +        userTO.setPassword("newPassword12");
 +
 +        AttrTO type = null;
 +        for (AttrTO attr : userTO.getPlainAttrs()) {
 +            if ("type".equals(attr.getSchema())) {
 +                type = attr;
 +            }
 +        }
 +        assertNotNull(type);
 +        userTO.getPlainAttrs().remove(type);
 +
 +        try {
 +            userTO = createUser(userTO);
 +            fail();
 +        } catch (SyncopeClientException e) {
 +            assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
 +        }
 +
 +        userTO.getPlainAttrs().add(type);
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +    }
 +
 +    @Test
 +    public void enforceMandatoryConditionOnDerived() {
 +        ResourceTO resourceTO = resourceService.read(RESOURCE_NAME_CSV);
 +        assertNotNull(resourceTO);
 +        resourceTO.setKey("resource-csv-enforcing");
 +        resourceTO.setEnforceMandatoryCondition(true);
 +
 +        Response response = resourceService.create(resourceTO);
 +        resourceTO = getObject(response.getLocation(), ResourceService.class, ResourceTO.class);
 +        assertNotNull(resourceTO);
 +
 +        UserTO userTO = getUniqueSampleTO("syncope222@apache.org");
 +        userTO.getResources().add(resourceTO.getKey());
 +        userTO.setPassword("newPassword12");
 +
 +        try {
 +            userTO = createUser(userTO);
 +            fail();
 +        } catch (SyncopeClientException e) {
 +            assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
 +        }
 +
 +        userTO.getAuxClasses().add("csv");
 +        userTO.getDerAttrs().add(attrTO("csvuserid", null));
 +
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +        assertEquals(Collections.singleton("resource-csv-enforcing"), userTO.getResources());
 +    }
 +
 +    @Test
 +    public void createUserWithDbPropagation() {
 +        UserTO userTO = getUniqueSampleTO("yyy@yyy.yyy");
 +        userTO.getResources().add(RESOURCE_NAME_TESTDB);
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +        assertEquals(1, userTO.getPropagationStatusTOs().size());
 +        assertTrue(userTO.getPropagationStatusTOs().get(0).getStatus().isSuccessful());
 +    }
 +
 +    @Test(expected = SyncopeClientException.class)
 +    public void createWithInvalidPassword() {
 +        UserTO userTO = getSampleTO("invalidpasswd@syncope.apache.org");
 +        userTO.setPassword("pass");
 +        createUser(userTO);
 +    }
 +
 +    @Test(expected = SyncopeClientException.class)
 +    public void createWithInvalidUsername() {
 +        UserTO userTO = getSampleTO("invalidusername@syncope.apache.org");
 +        userTO.setUsername("us");
 +        userTO.setRealm("/odd");
 +
 +        createUser(userTO);
 +    }
 +
 +    @Test(expected = SyncopeClientException.class)
 +    public void createWithInvalidPasswordByRes() {
 +        UserTO userTO = getSampleTO("invalidPwdByRes@passwd.com");
 +
 +        // configured to be minLength=16
 +        userTO.setPassword("password1");
 +        userTO.getResources().add(RESOURCE_NAME_NOPROPAGATION);
 +        createUser(userTO);
 +    }
 +
 +    @Test(expected = SyncopeClientException.class)
 +    public void createWithInvalidPasswordByGroup() {
 +        UserTO userTO = getSampleTO("invalidPwdByGroup@passwd.com");
 +
 +        // configured to be minLength=16
 +        userTO.setPassword("password1");
 +
 +        MembershipTO membership = new MembershipTO();
 +        membership.setRightKey(8L);
 +
 +        userTO.getMemberships().add(membership);
 +
 +        createUser(userTO);
 +    }
 +
 +    @Test(expected = SyncopeClientException.class)
 +    public void createWithException() {
 +        UserTO newUserTO = new UserTO();
 +        newUserTO.getPlainAttrs().add(attrTO("userId", "userId@nowhere.org"));
 +        createUser(newUserTO);
 +    }
 +
 +    @Test
 +    public void create() {
 +        // get task list
 +        PagedResult<PropagationTaskTO> tasks = taskService.list(
 +                TaskType.PROPAGATION,
 +                SyncopeClient.getListQueryBuilder().page(1).size(1).build());
 +        assertNotNull(tasks);
 +        assertFalse(tasks.getResult().isEmpty());
 +
 +        long maxKey = tasks.getResult().iterator().next().getKey();
 +        PropagationTaskTO taskTO = taskService.read(maxKey);
 +
 +        assertNotNull(taskTO);
 +        int maxTaskExecutions = taskTO.getExecutions().size();
 +
 +        UserTO userTO = getUniqueSampleTO("a.b@c.com");
 +
 +        // add a membership
 +        MembershipTO membershipTO = new MembershipTO();
 +        membershipTO.setRightKey(8L);
 +        userTO.getMemberships().add(membershipTO);
 +
 +        // add an attribute with a non-existing schema: must be ignored
 +        AttrTO attrWithInvalidSchemaTO = attrTO("invalid schema", "a value");
 +        userTO.getPlainAttrs().add(attrWithInvalidSchemaTO);
 +
 +        // add an attribute with null value: must be ignored
 +        userTO.getPlainAttrs().add(attrTO("activationDate", null));
 +
 +        // 1. create user
 +        UserTO newUserTO = createUser(userTO);
 +
 +        assertNotNull(newUserTO);
 +
 +        // issue SYNCOPE-15
 +        assertNotNull(newUserTO.getCreationDate());
 +        assertNotNull(newUserTO.getCreator());
 +        assertNotNull(newUserTO.getLastChangeDate());
 +        assertNotNull(newUserTO.getLastModifier());
 +        assertEquals(newUserTO.getCreationDate(), newUserTO.getLastChangeDate());
 +
 +        assertFalse(newUserTO.getPlainAttrs().contains(attrWithInvalidSchemaTO));
 +
 +        // check for changePwdDate
 +        assertNotNull(newUserTO.getCreationDate());
 +
 +        // 2. check for virtual attribute value
 +        newUserTO = userService.read(newUserTO.getKey());
 +        assertNotNull(newUserTO);
 +
 +        assertNotNull(newUserTO.getVirAttrMap());
 +        assertNotNull(newUserTO.getVirAttrMap().get("virtualdata").getValues());
 +        assertFalse(newUserTO.getVirAttrMap().get("virtualdata").getValues().isEmpty());
 +        assertEquals("virtualvalue", newUserTO.getVirAttrMap().get("virtualdata").getValues().get(0));
 +
 +        // get the new task list
 +        tasks = taskService.list(
 +                TaskType.PROPAGATION,
 +                SyncopeClient.getListQueryBuilder().page(1).size(1).build());
 +        assertNotNull(tasks);
 +        assertFalse(tasks.getResult().isEmpty());
 +
 +        long newMaxKey = tasks.getResult().iterator().next().getKey();
 +
 +        // default configuration for ws-target-resource2:
 +        // only failed executions have to be registered
 +        // --> no more tasks/executions should be added
 +        assertEquals(newMaxKey, maxKey);
 +
 +        // get last task
 +        taskTO = taskService.read(newMaxKey);
 +
 +        assertNotNull(taskTO);
 +        assertEquals(maxTaskExecutions, taskTO.getExecutions().size());
 +
 +        // 3. verify password
 +        try {
 +            Pair<Map<String, Set<String>>, UserTO> self =
 +                    clientFactory.create(newUserTO.getUsername(), "password123").self();
 +            assertNotNull(self);
 +        } catch (AccessControlException e) {
 +            fail("Credentials should be valid and not cause AccessControlException");
 +        }
 +
 +        UserSelfService userSelfService2 = clientFactory.create(
 +                newUserTO.getUsername(), "passwordXX").getService(UserSelfService.class);
 +        try {
 +            userSelfService2.read();
 +            fail("Credentials are invalid, thus request should raise AccessControlException");
 +        } catch (AccessControlException e) {
 +            assertNotNull(e);
 +        }
 +
 +        // 4. try (and fail) to create another user with same (unique) values
 +        userTO = getSampleTO(userTO.getUsername());
 +        AttrTO userIdAttr = userTO.getPlainAttrMap().get("userId");
 +        userIdAttr.getValues().clear();
 +        userIdAttr.getValues().add("a.b@c.com");
 +
 +        try {
 +            createUser(userTO);
 +            fail();
 +        } catch (SyncopeClientException e) {
 +            assertEquals(ClientExceptionType.GenericPersistence, e.getType());
 +        }
 +    }
 +
 +    @Test
 +    public void createWithRequiredValueMissing() {
 +        UserTO userTO = getSampleTO("a.b@c.it");
 +
 +        AttrTO type = userTO.getPlainAttrMap().get("type");
 +        userTO.getPlainAttrs().remove(type);
 +
 +        MembershipTO membershipTO = new MembershipTO();
 +        membershipTO.setRightKey(8L);
 +        userTO.getMemberships().add(membershipTO);
 +
 +        // 1. create user without type (mandatory by UserSchema)
 +        try {
 +            createUser(userTO);
 +            fail();
 +        } catch (SyncopeClientException e) {
 +            assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
 +        }
 +
 +        userTO.getPlainAttrs().add(attrTO("type", "F"));
 +
 +        AttrTO surname = userTO.getPlainAttrMap().get("surname");
 +        userTO.getPlainAttrs().remove(surname);
 +
 +        // 2. create user without surname (mandatory when type == 'F')
 +        try {
 +            createUser(userTO);
 +            fail();
 +        } catch (SyncopeClientException e) {
 +            assertEquals(ClientExceptionType.RequiredValuesMissing, e.getType());
 +        }
 +    }
 +
 +    @Test
 +    public void delete() {
 +        try {
 +            userService.delete(0L);
 +        } catch (SyncopeClientException e) {
 +            assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
 +        }
 +
 +        UserTO userTO = getSampleTO("qqgf.z@nn.com");
 +
 +        // specify a propagation
 +        userTO.getResources().add(RESOURCE_NAME_TESTDB);
 +
 +        userTO = createUser(userTO);
 +
 +        long id = userTO.getKey();
 +
 +        userTO = deleteUser(id);
 +
 +        assertNotNull(userTO);
 +        assertEquals(id, userTO.getKey());
 +        assertTrue(userTO.getPlainAttrs().isEmpty());
 +
 +        // check for propagation result
 +        assertFalse(userTO.getPropagationStatusTOs().isEmpty());
 +        assertTrue(userTO.getPropagationStatusTOs().get(0).getStatus().isSuccessful());
 +
 +        try {
 +            userService.delete(userTO.getKey());
 +        } catch (SyncopeClientException e) {
 +            assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
 +        }
 +    }
 +
 +    @Test
 +    public void deleteByUsername() {
 +        UserTO userTO = getSampleTO("delete.by.username@apache.org");
 +
 +        // specify a propagation
 +        userTO.getResources().add(RESOURCE_NAME_TESTDB);
 +
 +        userTO = createUser(userTO);
 +
 +        long id = userTO.getKey();
 +        userTO = userService.read(id);
 +        userTO = deleteUser(userTO.getKey());
 +
 +        assertNotNull(userTO);
 +        assertEquals(id, userTO.getKey());
 +        assertTrue(userTO.getPlainAttrs().isEmpty());
 +
 +        // check for propagation result
 +        assertFalse(userTO.getPropagationStatusTOs().isEmpty());
 +        assertTrue(userTO.getPropagationStatusTOs().get(0).getStatus().isSuccessful());
 +
 +        try {
 +            userService.read(userTO.getKey());
 +        } catch (SyncopeClientException e) {
 +            assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
 +        }
 +    }
 +
 +    @Test
 +    public void list() {
 +        PagedResult<UserTO> users = userService.list(
 +                SyncopeClient.getAnyListQueryBuilder().realm(SyncopeConstants.ROOT_REALM).build());
 +        assertNotNull(users);
 +        assertFalse(users.getResult().isEmpty());
 +
 +        for (UserTO user : users.getResult()) {
 +            assertNotNull(user);
 +        }
 +    }
 +
 +    @Test
 +    public void paginatedList() {
 +        PagedResult<UserTO> users = userService.list(
 +                SyncopeClient.getAnyListQueryBuilder().realm(SyncopeConstants.ROOT_REALM).page(1).size(2).build());
 +        assertNotNull(users);
 +        assertFalse(users.getResult().isEmpty());
 +        assertEquals(2, users.getResult().size());
 +
 +        for (UserTO user : users.getResult()) {
 +            assertNotNull(user);
 +        }
 +
 +        users = userService.list(SyncopeClient.getAnyListQueryBuilder().realm(SyncopeConstants.ROOT_REALM).
 +                page(2).size(2).build());
 +        assertNotNull(users);
 +        assertEquals(2, users.getPage());
 +        assertEquals(2, users.getResult().size());
 +
 +        users = userService.list(SyncopeClient.getAnyListQueryBuilder().realm(SyncopeConstants.ROOT_REALM).
 +                page(100).size(2).build());
 +        assertNotNull(users);
 +        assertTrue(users.getResult().isEmpty());
 +    }
 +
 +    @Test
 +    public void read() {
 +        UserTO userTO = userService.read(1L);
 +
 +        assertNotNull(userTO);
 +        assertNotNull(userTO.getPlainAttrs());
 +        assertFalse(userTO.getPlainAttrs().isEmpty());
 +    }
 +
 +    @Test
 +    public void readWithMailAddressAsUserName() {
 +        UserTO userTO = createUser(getUniqueSampleTO("mail@domain.org"));
 +        userTO = userService.read(userTO.getKey());
 +        assertNotNull(userTO);
 +    }
 +
 +    @Test
 +    public void updateWithouPassword() {
 +        UserTO userTO = getUniqueSampleTO("updatewithout@password.com");
 +
 +        userTO = createUser(userTO);
 +
 +        assertNotNull(userTO);
 +
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +        userMod.getDerAttrsToRemove().add("cn");
 +
 +        userTO = updateUser(userMod);
 +
 +        assertNotNull(userTO);
 +        assertNotNull(userTO.getDerAttrMap());
 +        assertFalse(userTO.getDerAttrMap().containsKey("cn"));
 +    }
 +
 +    @Test(expected = SyncopeClientException.class)
 +    public void updateInvalidPassword() {
 +        UserTO userTO = getSampleTO("updateinvalid@password.com");
 +
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +        userMod.setPassword("pass");
 +
 +        userService.update(userMod);
 +    }
 +
 +    @Test(expected = SyncopeClientException.class)
 +    public void updateSamePassword() {
 +        UserTO userTO = getUniqueSampleTO("updatesame@password.com");
 +        userTO.setRealm("/even/two");
 +
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +        userMod.setPassword("password123");
 +
 +        userService.update(userMod);
 +    }
 +
 +    @Test
 +    public void update() {
 +        UserTO userTO = getUniqueSampleTO("g.h@t.com");
 +
 +        MembershipTO membershipTO = new MembershipTO();
 +        membershipTO.setRightKey(8L);
 +        userTO.getMemberships().add(membershipTO);
 +
 +        userTO = createUser(userTO);
 +
 +        assertFalse(userTO.getDerAttrs().isEmpty());
 +        assertEquals(1, userTO.getMemberships().size());
 +
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +        userMod.setPassword("new2Password");
 +
 +        userMod.getPlainAttrsToRemove().add("userId");
 +        String newUserId = getUUIDString() + "t.w@spre.net";
 +        userMod.getPlainAttrsToUpdate().add(attrMod("userId", newUserId));
 +
 +        userMod.getPlainAttrsToRemove().add("fullname");
 +        String newFullName = getUUIDString() + "g.h@t.com";
 +        userMod.getPlainAttrsToUpdate().add(attrMod("fullname", newFullName));
 +
 +        userMod.getDerAttrsToAdd().add("cn");
 +        userMod.getMembershipsToAdd().add(8L);
 +        userMod.getMembershipsToRemove().add(userTO.getMemberships().get(0).getRightKey());
 +
 +        userTO = updateUser(userMod);
 +        assertNotNull(userTO);
 +
 +        // issue SYNCOPE-15
 +        assertNotNull(userTO.getCreationDate());
 +        assertNotNull(userTO.getCreator());
 +        assertNotNull(userTO.getLastChangeDate());
 +        assertNotNull(userTO.getLastModifier());
 +        assertTrue(userTO.getCreationDate().before(userTO.getLastChangeDate()));
 +
 +        assertEquals(1, userTO.getMemberships().size());
 +        assertFalse(userTO.getDerAttrs().isEmpty());
 +
 +        AttrTO userIdAttr = userTO.getPlainAttrMap().get("userId");
 +        assertEquals(Collections.singletonList(newUserId), userIdAttr.getValues());
 +
 +        AttrTO fullNameAttr = userTO.getPlainAttrMap().get("fullname");
 +        assertEquals(Collections.singletonList(newFullName), fullNameAttr.getValues());
 +    }
 +
 +    @Test
 +    public void updatePasswordOnly() {
 +        int beforeTasks = taskService.list(TaskType.PROPAGATION,
 +                SyncopeClient.getListQueryBuilder().page(1).size(1).build()).getTotalCount();
 +        assertFalse(beforeTasks <= 0);
 +
 +        UserTO userTO = getUniqueSampleTO("pwdonly@t.com");
 +        MembershipTO membershipTO = new MembershipTO();
 +        membershipTO.setRightKey(8L);
 +        userTO.getMemberships().add(membershipTO);
 +
 +        userTO = createUser(userTO);
 +
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +        userMod.setPassword("newPassword123");
 +
 +        userTO = updateUser(userMod);
 +
 +        // check for changePwdDate
 +        assertNotNull(userTO.getChangePwdDate());
 +
 +        int afterTasks = taskService.list(TaskType.PROPAGATION,
 +                SyncopeClient.getListQueryBuilder().page(1).size(1).build()).getTotalCount();
 +        assertFalse(beforeTasks <= 0);
 +
 +        assertTrue(beforeTasks < afterTasks);
 +    }
 +
 +    @SuppressWarnings("unchecked")
 +    @Test
 +    public void verifyTaskRegistration() {
 +        // get task list
 +        PagedResult<PropagationTaskTO> tasks = taskService.list(
 +                TaskType.PROPAGATION,
 +                SyncopeClient.getListQueryBuilder().page(1).size(1).build());
 +        assertNotNull(tasks);
 +        assertFalse(tasks.getResult().isEmpty());
 +
 +        long maxKey = tasks.getResult().iterator().next().getKey();
 +
 +        // --------------------------------------
 +        // Create operation
 +        // --------------------------------------
 +        UserTO userTO = getUniqueSampleTO("t@p.mode");
 +
 +        // add a membership
 +        MembershipTO membershipTO = new MembershipTO();
 +        membershipTO.setRightKey(8L);
 +        userTO.getMemberships().add(membershipTO);
 +
 +        // 1. create user
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +
 +        // get the new task list
 +        tasks = taskService.list(
 +                TaskType.PROPAGATION,
 +                SyncopeClient.getListQueryBuilder().page(1).size(1).build());
 +        assertNotNull(tasks);
 +        assertFalse(tasks.getResult().isEmpty());
 +
 +        long newMaxKey = tasks.getResult().iterator().next().getKey();
 +
 +        // default configuration for ws-target-resource2:
 +        // only failed executions have to be registered
 +        // --> no more tasks/executions should be added
 +        assertEquals(newMaxKey, maxKey);
 +
 +        // --------------------------------------
 +        // Update operation
 +        // --------------------------------------
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +
 +        userMod.getPlainAttrsToUpdate().add(attrMod("surname", "surname"));
 +
 +        userTO = updateUser(userMod);
 +
 +        assertNotNull(userTO);
 +
 +        // get the new task list
 +        tasks = taskService.list(
 +                TaskType.PROPAGATION,
 +                SyncopeClient.getListQueryBuilder().page(1).size(1).build());
 +
 +        maxKey = newMaxKey;
 +        newMaxKey = tasks.getResult().iterator().next().getKey();
 +
 +        // default configuration for ws-target-resource2:
 +        // all update executions have to be registered
 +        assertTrue(newMaxKey > maxKey);
 +
 +        final PropagationTaskTO taskTO = taskService.read(newMaxKey);
 +
 +        assertNotNull(taskTO);
 +        assertEquals(1, taskTO.getExecutions().size());
 +
 +        // --------------------------------------
 +        // Delete operation
 +        // --------------------------------------
 +        userService.delete(userTO.getKey());
 +
 +        // get the new task list
 +        tasks = taskService.list(
 +                TaskType.PROPAGATION,
 +                SyncopeClient.getListQueryBuilder().page(1).size(1).build());
 +
 +        maxKey = newMaxKey;
 +        newMaxKey = tasks.getResult().iterator().next().getKey();
 +
 +        // default configuration for ws-target-resource2: no delete executions have to be registered
 +        // --> no more tasks/executions should be added
 +        assertEquals(newMaxKey, maxKey);
 +    }
 +
 +    @Test
 +    public void createActivate() {
 +        Assume.assumeTrue(ActivitiDetector.isActivitiEnabledForUsers(syncopeService));
 +
 +        UserTO userTO = getUniqueSampleTO("createActivate@syncope.apache.org");
 +
 +        MembershipTO membershipTO = new MembershipTO();
 +        membershipTO.setRightKey(11L);
 +        userTO.getMemberships().add(membershipTO);
 +
 +        userTO = createUser(userTO);
 +
 +        assertNotNull(userTO);
 +        assertNotNull(userTO.getToken());
 +        assertNotNull(userTO.getTokenExpireTime());
 +
 +        assertEquals("created", userTO.getStatus());
 +
 +        StatusMod statusMod = new StatusMod();
 +        statusMod.setKey(userTO.getKey());
 +        statusMod.setType(StatusMod.ModType.ACTIVATE);
 +        statusMod.setToken(userTO.getToken());
 +        userTO = userService.status(statusMod).readEntity(UserTO.class);
 +
 +        assertNotNull(userTO);
 +        assertNull(userTO.getToken());
 +        assertNull(userTO.getTokenExpireTime());
 +        assertEquals("active", userTO.getStatus());
 +    }
 +
 +    @Test
 +    public void suspendReactivate() {
 +        UserTO userTO = getUniqueSampleTO("suspendReactivate@syncope.apache.org");
 +
 +        MembershipTO membershipTO = new MembershipTO();
 +        membershipTO.setRightKey(7L);
 +        userTO.getMemberships().add(membershipTO);
 +
 +        userTO = createUser(userTO);
 +
 +        assertNotNull(userTO);
 +        assertEquals(ActivitiDetector.isActivitiEnabledForUsers(syncopeService)
 +                ? "active"
 +                : "created", userTO.getStatus());
 +
 +        StatusMod statusMod = new StatusMod();
 +        statusMod.setKey(userTO.getKey());
 +        statusMod.setType(StatusMod.ModType.SUSPEND);
 +        userTO = userService.status(statusMod).readEntity(UserTO.class);
 +        assertNotNull(userTO);
 +        assertEquals("suspended", userTO.getStatus());
 +
 +        statusMod = new StatusMod();
 +        statusMod.setKey(userTO.getKey());
 +        statusMod.setType(StatusMod.ModType.REACTIVATE);
 +        userTO = userService.status(statusMod).readEntity(UserTO.class);
 +        assertNotNull(userTO);
 +        assertEquals("active", userTO.getStatus());
 +    }
 +
 +    @Test
 +    public void suspendReactivateOnResource() {
 +        // Assert resources are present
 +        ResourceTO dbTable = resourceService.read(RESOURCE_NAME_TESTDB);
 +        assertNotNull(dbTable);
 +        ResourceTO ldap = resourceService.read(RESOURCE_NAME_LDAP);
 +        assertNotNull(ldap);
 +
 +        // Create user with reference to resources
 +        UserTO userTO = getUniqueSampleTO("suspreactonresource@syncope.apache.org");
 +        userTO.getMemberships().clear();
 +        userTO.getResources().clear();
 +        userTO.getResources().add(RESOURCE_NAME_TESTDB);
 +        userTO.getResources().add(RESOURCE_NAME_LDAP);
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +        assertEquals(ActivitiDetector.isActivitiEnabledForUsers(syncopeService)
 +                ? "active"
 +                : "created", userTO.getStatus());
 +        long userKey = userTO.getKey();
 +
 +        // Suspend with effect on syncope, ldap and db => user should be suspended in syncope and all resources
 +        StatusMod statusMod = new StatusMod();
 +        statusMod.setKey(userKey);
 +        statusMod.setType(StatusMod.ModType.SUSPEND);
 +        statusMod.setOnSyncope(true);
-         statusMod.getResourceNames().add(RESOURCE_NAME_TESTDB);
-         statusMod.getResourceNames().add(RESOURCE_NAME_LDAP);
++        statusMod.getResources().add(RESOURCE_NAME_TESTDB);
++        statusMod.getResources().add(RESOURCE_NAME_LDAP);
 +        userTO = userService.status(statusMod).readEntity(UserTO.class);
 +        assertNotNull(userTO);
 +        assertEquals("suspended", userTO.getStatus());
 +
 +        ConnObjectTO connObjectTO =
 +                resourceService.readConnObject(RESOURCE_NAME_TESTDB, AnyTypeKind.USER.name(), userKey);
 +        assertFalse(getBooleanAttribute(connObjectTO, OperationalAttributes.ENABLE_NAME));
 +
 +        connObjectTO = resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), userKey);
 +        assertNotNull(connObjectTO);
 +
 +        // Suspend and reactivate only on ldap => db and syncope should still show suspended
 +        statusMod = new StatusMod();
 +        statusMod.setKey(userKey);
 +        statusMod.setType(StatusMod.ModType.SUSPEND);
 +        statusMod.setOnSyncope(false);
-         statusMod.getResourceNames().add(RESOURCE_NAME_LDAP);
++        statusMod.getResources().add(RESOURCE_NAME_LDAP);
 +        userService.status(statusMod);
 +        statusMod.setType(StatusMod.ModType.REACTIVATE);
 +        userTO = userService.status(statusMod).readEntity(UserTO.class);
 +        assertNotNull(userTO);
 +        assertEquals("suspended", userTO.getStatus());
 +
 +        connObjectTO = resourceService.readConnObject(RESOURCE_NAME_TESTDB, AnyTypeKind.USER.name(), userKey);
 +        assertFalse(getBooleanAttribute(connObjectTO, OperationalAttributes.ENABLE_NAME));
 +
 +        // Reactivate on syncope and db => syncope and db should show the user as active
 +        statusMod = new StatusMod();
 +        statusMod.setKey(userKey);
 +        statusMod.setType(StatusMod.ModType.REACTIVATE);
 +        statusMod.setOnSyncope(true);
-         statusMod.getResourceNames().add(RESOURCE_NAME_TESTDB);
++        statusMod.getResources().add(RESOURCE_NAME_TESTDB);
 +
 +        userTO = userService.status(statusMod).readEntity(UserTO.class);
 +        assertNotNull(userTO);
 +        assertEquals("active", userTO.getStatus());
 +
 +        connObjectTO = resourceService.readConnObject(RESOURCE_NAME_TESTDB, AnyTypeKind.USER.name(), userKey);
 +        assertTrue(getBooleanAttribute(connObjectTO, OperationalAttributes.ENABLE_NAME));
 +    }
 +
 +    @Test
 +    public void updateMultivalueAttribute() {
 +        UserTO userTO = getUniqueSampleTO("multivalue@syncope.apache.org");
 +        userTO.getResources().clear();
 +        userTO.getDerAttrs().clear();
 +        userTO.getVirAttrs().clear();
 +
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +
 +        AttrTO loginDate = userTO.getPlainAttrMap().get("loginDate");
 +        assertNotNull(loginDate);
 +        assertEquals(1, loginDate.getValues().size());
 +
 +        UserMod userMod = new UserMod();
 +
 +        userMod.setKey(userTO.getKey());
 +        userMod.getPlainAttrsToUpdate().add(attrMod("loginDate", "2000-01-01"));
 +
 +        userTO = updateUser(userMod);
 +        assertNotNull(userTO);
 +
 +        loginDate = userTO.getPlainAttrMap().get("loginDate");
 +        assertNotNull(loginDate);
 +        assertEquals(2, loginDate.getValues().size());
 +    }
 +
 +    @Test(expected = EmptyResultDataAccessException.class)
 +    public void issue213() {
 +        UserTO userTO = getUniqueSampleTO("issue213@syncope.apache.org");
 +        userTO.getResources().add(RESOURCE_NAME_TESTDB);
 +
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +        assertEquals(1, userTO.getResources().size());
 +
 +        JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
 +
 +        String username = jdbcTemplate.queryForObject("SELECT id FROM test WHERE id=?", String.class,
 +                userTO.getUsername());
 +
 +        assertEquals(userTO.getUsername(), username);
 +
 +        UserMod userMod = new UserMod();
 +
 +        userMod.setKey(userTO.getKey());
 +        userMod.getResourcesToRemove().add(RESOURCE_NAME_TESTDB);
 +
 +        userTO = updateUser(userMod);
 +        assertTrue(userTO.getResources().isEmpty());
 +
 +        jdbcTemplate.queryForObject("SELECT id FROM test WHERE id=?", String.class, userTO.getUsername());
 +    }
 +
 +    @Test
 +    public void issue234() {
 +        UserTO inUserTO = getUniqueSampleTO("issue234@syncope.apache.org");
 +        inUserTO.getResources().add(RESOURCE_NAME_LDAP);
 +
 +        UserTO userTO = createUser(inUserTO);
 +        assertNotNull(userTO);
 +
 +        UserMod userMod = new UserMod();
 +
 +        userMod.setKey(userTO.getKey());
 +        userMod.setUsername("1" + userTO.getUsername());
 +
 +        userTO = updateUser(userMod);
 +        assertNotNull(userTO);
 +        assertEquals("1" + inUserTO.getUsername(), userTO.getUsername());
 +    }
 +
 +    @Test
 +    public void issue270() {
 +        // 1. create a new user without virtual attributes
 +        UserTO original = getUniqueSampleTO("issue270@syncope.apache.org");
 +        // be sure to remove all virtual attributes
 +        original.getVirAttrs().clear();
 +
 +        original = createUser(original);
 +
 +        assertNotNull(original);
 +
 +        assertTrue(original.getVirAttrs().isEmpty());
 +
 +        UserTO toBeUpdated = userService.read(original.getKey());
 +
 +        AttrTO virtual = attrTO("virtualdata", "virtualvalue");
 +        toBeUpdated.getVirAttrs().add(virtual);
 +
 +        // 2. try to update by adding a resource, but no password: must fail
 +        UserMod userMod = AnyOperations.diff(toBeUpdated, original);
 +        assertNotNull(userMod);
 +
 +        toBeUpdated = updateUser(userMod);
 +        assertNotNull(toBeUpdated);
 +
 +        assertFalse(toBeUpdated.getVirAttrs().isEmpty());
 +        assertNotNull(toBeUpdated.getVirAttrs().iterator().next());
 +
 +        assertEquals(virtual.getSchema(), toBeUpdated.getVirAttrs().iterator().next().getSchema());
 +    }
 +
 +    @Test
 +    public final void issue280() {
 +        UserTO userTO = getUniqueSampleTO("issue280@syncope.apache.org");
 +        userTO.getResources().clear();
 +        userTO.getMemberships().clear();
 +        userTO.getDerAttrs().clear();
 +
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +        userMod.setPassword("123password");
 +        userMod.getResourcesToAdd().add(RESOURCE_NAME_TESTDB);
 +
 +        final StatusMod st = new StatusMod();
 +        st.setOnSyncope(false);
-         st.getResourceNames().add(RESOURCE_NAME_TESTDB);
++        st.getResources().add(RESOURCE_NAME_TESTDB);
 +        userMod.setPwdPropRequest(st);
 +
 +        userTO = updateUser(userMod);
 +        assertNotNull(userTO);
 +
 +        final List<PropagationStatus> propagations = userTO.getPropagationStatusTOs();
 +
 +        assertNotNull(propagations);
 +        assertEquals(1, propagations.size());
 +
 +        final PropagationTaskExecStatus status = propagations.get(0).getStatus();
 +        final String resource = propagations.get(0).getResource();
 +
 +        assertNotNull(status);
 +        assertEquals(RESOURCE_NAME_TESTDB, resource);
 +        assertTrue(status.isSuccessful());
 +    }
 +
 +    @Test
 +    public void issue281() {
 +        UserTO userTO = getUniqueSampleTO("issue281@syncope.apache.org");
 +        userTO.getResources().clear();
 +        userTO.getMemberships().clear();
 +        userTO.getDerAttrs().clear();
 +        userTO.getResources().add(RESOURCE_NAME_CSV);
 +
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +
 +        final List<PropagationStatus> propagations = userTO.getPropagationStatusTOs();
 +
 +        assertNotNull(propagations);
 +        assertEquals(1, propagations.size());
 +
 +        final PropagationTaskExecStatus status = propagations.get(0).getStatus();
 +        final String resource = propagations.get(0).getResource();
 +
 +        assertNotNull(status);
 +        assertEquals(RESOURCE_NAME_CSV, resource);
 +        assertFalse(status.isSuccessful());
 +    }
 +
 +    @Test
 +    public void issue288() {
 +        UserTO userTO = getSampleTO("issue288@syncope.apache.org");
 +        userTO.getPlainAttrs().add(attrTO("aLong", "STRING"));
 +
 +        try {
 +            createUser(userTO);
 +            fail();
 +        } catch (SyncopeClientException e) {
 +            assertEquals(ClientExceptionType.InvalidValues, e.getType());
 +        }
 +    }
 +
 +    @Test
 +    public void groupAttrPropagation() {
 +        UserTO userTO = getUniqueSampleTO("checkGroupAttrPropagation@syncope.apache.org");
 +        userTO.getResources().clear();
 +        userTO.getMemberships().clear();
 +        userTO.getDerAttrs().clear();
 +        userTO.getVirAttrs().clear();
 +
 +        userTO.getAuxClasses().add("csv");
 +        userTO.getDerAttrs().add(attrTO("csvuserid", null));
 +
 +        MembershipTO membershipTO = new MembershipTO();
 +        membershipTO.setRightKey(1L);
 +
 +        userTO.getMemberships().add(membershipTO);
 +
 +        userTO.getResources().add(RESOURCE_NAME_CSV);
 +
 +        UserTO actual = createUser(userTO);
 +        assertNotNull(actual);
 +        assertNotNull(actual.getDerAttrMap().get("csvuserid"));
 +
 +        ConnObjectTO connObjectTO =
 +                resourceService.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), actual.getKey());
 +        assertNotNull(connObjectTO);
 +        assertEquals("sx-dx", connObjectTO.getPlainAttrMap().get("THEIRGROUP").getValues().get(0));
 +    }
 +
 +    @Test
 +    public void noContent() throws IOException {
 +        SyncopeClient noContentclient = clientFactory.create(ADMIN_UNAME, ADMIN_PWD);
 +        UserService noContentService = noContentclient.prefer(UserService.class, Preference.RETURN_NO_CONTENT);
 +
 +        UserTO user = getUniqueSampleTO("nocontent@syncope.apache.org");
 +
 +        Response response = noContentService.create(user, true);
 +        assertEquals(Response.Status.CREATED.getStatusCode(), response.getStatus());
 +        assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
 +        assertEquals(StringUtils.EMPTY, IOUtils.toString((InputStream) response.getEntity()));
 +
 +        user = getObject(response.getLocation(), UserService.class, UserTO.class);
 +        assertNotNull(user);
 +
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(user.getKey());
 +        userMod.setPassword("password321");
 +
 +        response = noContentService.update(userMod);
 +        assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
 +        assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
 +        assertEquals(StringUtils.EMPTY, IOUtils.toString((InputStream) response.getEntity()));
 +
 +        response = noContentService.delete(user.getKey());
 +        assertEquals(Response.Status.NO_CONTENT.getStatusCode(), response.getStatus());
 +        assertEquals(Preference.RETURN_NO_CONTENT.toString(), response.getHeaderString(RESTHeaders.PREFERENCE_APPLIED));
 +        assertEquals(StringUtils.EMPTY, IOUtils.toString((InputStream) response.getEntity()));
 +    }
 +
 +    @Test
 +    public void customPolicyRules() {
 +        // Using custom policy rules with application/xml requires to overwrite
 +        // org.apache.syncope.common.lib.policy.AbstractAccountRuleConf's and / or
 +        // org.apache.syncope.common.lib.policy.AbstractPasswordRuleConf's
 +        // @XmlSeeAlso - the power of JAXB :-/
 +        Assume.assumeTrue(MediaType.APPLICATION_JSON_TYPE.equals(clientFactory.getContentType().getMediaType()));
 +
 +        AccountPolicyTO accountPolicy = new AccountPolicyTO();
 +        accountPolicy.setDescription("Account Policy with custom rules");
 +        accountPolicy.getRuleConfs().add(new TestAccountRuleConf());
 +        accountPolicy = createPolicy(accountPolicy);
 +        assertNotNull(accountPolicy);
 +
 +        PasswordPolicyTO passwordPolicy = new PasswordPolicyTO();
 +        passwordPolicy.setDescription("Password Policy with custom rules");
 +        passwordPolicy.getRuleConfs().add(new TestPasswordRuleConf());
 +        passwordPolicy = createPolicy(passwordPolicy);
 +        assertNotNull(passwordPolicy);
 +
 +        RealmTO realm = realmService.list("/even/two").get(0);
 +        Long oldAccountPolicy = realm.getAccountPolicy();
 +        realm.setAccountPolicy(accountPolicy.getKey());
 +        Long oldPasswordPolicy = realm.getPasswordPolicy();
 +        realm.setPasswordPolicy(passwordPolicy.getKey());
 +        realmService.update(realm);
 +
 +        try {
 +            UserTO user = getUniqueSampleTO("custompolicyrules@syncope.apache.org");
 +            user.setRealm(realm.getFullPath());
 +            try {
 +                createUser(user);
 +                fail();
 +            } catch (SyncopeClientException e) {
 +                assertEquals(ClientExceptionType.InvalidUser, e.getType());
 +                assertTrue(e.getElements().iterator().next().startsWith("InvalidPassword"));
 +            }
 +
 +            user.setPassword(user.getPassword() + "XXX");
 +            try {
 +                createUser(user);
 +                fail();
 +            } catch (SyncopeClientException e) {
 +                assertEquals(ClientExceptionType.InvalidUser, e.getType());
 +                assertTrue(e.getElements().iterator().next().startsWith("InvalidUsername"));
 +            }
 +
 +            user.setUsername("YYY" + user.getUsername());
 +            user = createUser(user);
 +            assertNotNull(user);
 +        } finally {
 +            realm.setAccountPolicy(oldAccountPolicy);
 +            realm.setPasswordPolicy(oldPasswordPolicy);
 +            realmService.update(realm);
 +
 +            policyService.delete(passwordPolicy.getKey());
 +            policyService.delete(accountPolicy.getKey());
 +        }
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE108() {
 +        UserTO userTO = getUniqueSampleTO("syncope108@syncope.apache.org");
 +        userTO.getResources().clear();
 +        userTO.getMemberships().clear();
 +        userTO.getDerAttrs().clear();
 +        userTO.getVirAttrs().clear();
 +        userTO.getAuxClasses().add("csv");
 +        userTO.getDerAttrs().add(attrTO("csvuserid", null));
 +
 +        MembershipTO memb12 = new MembershipTO();
 +        memb12.setRightKey(12L);
 +
 +        userTO.getMemberships().add(memb12);
 +
 +        MembershipTO memb13 = new MembershipTO();
 +        memb13.setRightKey(13L);
 +
 +        userTO.getMemberships().add(memb13);
 +
 +        userTO.getResources().add(RESOURCE_NAME_CSV);
 +
 +        UserTO actual = createUser(userTO);
 +        assertNotNull(actual);
 +        assertEquals(2, actual.getMemberships().size());
 +        assertEquals(1, actual.getResources().size());
 +
 +        ConnObjectTO connObjectTO =
 +                resourceService.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), actual.getKey());
 +        assertNotNull(connObjectTO);
 +
 +        // -----------------------------------
 +        // Remove the first membership: de-provisioning shouldn't happen
 +        // -----------------------------------
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(actual.getKey());
 +
 +        userMod.getMembershipsToRemove().add(actual.getMemberships().get(0).getRightKey());
 +
 +        actual = updateUser(userMod);
 +        assertNotNull(actual);
 +        assertEquals(1, actual.getMemberships().size());
 +
 +        connObjectTO = resourceService.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), actual.getKey());
 +        assertNotNull(connObjectTO);
 +        // -----------------------------------
 +
 +        // -----------------------------------
 +        // Remove the resource assigned directly: de-provisioning shouldn't happen
 +        // -----------------------------------
 +        userMod = new UserMod();
 +        userMod.setKey(actual.getKey());
 +
 +        userMod.getResourcesToRemove().add(actual.getResources().iterator().next());
 +
 +        actual = updateUser(userMod);
 +        assertNotNull(actual);
 +        assertEquals(1, actual.getMemberships().size());
 +        assertFalse(actual.getResources().isEmpty());
 +
 +        connObjectTO = resourceService.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), actual.getKey());
 +        assertNotNull(connObjectTO);
 +        // -----------------------------------
 +
 +        // -----------------------------------
 +        // Remove the first membership: de-provisioning should happen
 +        // -----------------------------------
 +        userMod = new UserMod();
 +        userMod.setKey(actual.getKey());
 +
 +        userMod.getMembershipsToRemove().add(actual.getMemberships().get(0).getRightKey());
 +
 +        actual = updateUser(userMod);
 +        assertNotNull(actual);
 +        assertTrue(actual.getMemberships().isEmpty());
 +        assertTrue(actual.getResources().isEmpty());
 +
 +        try {
 +            resourceService.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), actual.getKey());
 +            fail("Read should not succeeed");
 +        } catch (SyncopeClientException e) {
 +            assertEquals(ClientExceptionType.NotFound, e.getType());
 +        }
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE185() {
 +        // 1. create user with LDAP resource, succesfully propagated
 +        UserTO userTO = getSampleTO("syncope185@syncope.apache.org");
 +        userTO.getVirAttrs().clear();
 +        userTO.getResources().add(RESOURCE_NAME_LDAP);
 +
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +        assertFalse(userTO.getPropagationStatusTOs().isEmpty());
 +        assertEquals(RESOURCE_NAME_LDAP, userTO.getPropagationStatusTOs().get(0).getResource());
 +        assertEquals(PropagationTaskExecStatus.SUCCESS, userTO.getPropagationStatusTOs().get(0).getStatus());
 +
 +        // 2. delete this user
 +        userService.delete(userTO.getKey());
 +
 +        // 3. try (and fail) to find this user on the external LDAP resource
 +        try {
 +            resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), userTO.getKey());
 +            fail("This entry should not be present on this resource");
 +        } catch (SyncopeClientException e) {
 +            assertEquals(ClientExceptionType.NotFound, e.getType());
 +        }
 +    }
 +
 +    @Test()
 +    public void issueSYNCOPE51() {
 +        AttrTO defaultCA = configurationService.get("password.cipher.algorithm");
 +        final String originalCAValue = defaultCA.getValues().get(0);
 +        defaultCA.getValues().set(0, "MD5");
 +        configurationService.set(defaultCA);
 +
 +        AttrTO newCA = configurationService.get(defaultCA.getSchema());
 +        assertEquals(defaultCA, newCA);
 +
 +        UserTO userTO = getSampleTO("syncope51@syncope.apache.org");
 +        userTO.setPassword("password");
 +
 +        try {
 +            createUser(userTO);
 +            fail("Create user should not succeed");
 +        } catch (SyncopeClientException e) {
 +            assertEquals(ClientExceptionType.NotFound, e.getType());
 +            assertTrue(e.getElements().iterator().next().contains("MD5"));
 +        }
 +
 +        defaultCA.getValues().set(0, originalCAValue);
 +        configurationService.set(defaultCA);
 +
 +        AttrTO oldCA = configurationService.get(defaultCA.getSchema());
 +        assertEquals(defaultCA, oldCA);
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE267() {
 +        // ----------------------------------
 +        // create user and check virtual attribute value propagation
 +        // ----------------------------------
 +        UserTO userTO = getUniqueSampleTO("syncope267@apache.org");
 +        userTO.getResources().clear();
 +        userTO.getResources().add(RESOURCE_NAME_DBVIRATTR);
 +
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +        assertFalse(userTO.getPropagationStatusTOs().isEmpty());
 +        assertEquals(RESOURCE_NAME_DBVIRATTR, userTO.getPropagationStatusTOs().get(0).getResource());
 +        assertEquals(PropagationTaskExecStatus.SUBMITTED, userTO.getPropagationStatusTOs().get(0).getStatus());
 +
 +        ConnObjectTO connObjectTO =
 +                resourceService.readConnObject(RESOURCE_NAME_DBVIRATTR, AnyTypeKind.USER.name(), userTO.getKey());
 +        assertNotNull(connObjectTO);
 +        assertEquals("virtualvalue", connObjectTO.getPlainAttrMap().get("USERNAME").getValues().get(0));
 +        // ----------------------------------
 +
 +        userTO = userService.read(userTO.getKey());
 +
 +        assertNotNull(userTO);
 +        assertEquals(1, userTO.getVirAttrs().size());
 +        assertEquals("virtualvalue", userTO.getVirAttrs().iterator().next().getValues().get(0));
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE266() {
 +        UserTO userTO = getUniqueSampleTO("syncope266@apache.org");
 +        userTO.getResources().clear();
 +
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +
 +        // this resource has not a mapping for Password
 +        userMod.getResourcesToAdd().add(RESOURCE_NAME_UPDATE);
 +
 +        userTO = updateUser(userMod);
 +        assertNotNull(userTO);
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE279() {
 +        UserTO userTO = getUniqueSampleTO("syncope279@apache.org");
 +        userTO.getResources().clear();
 +        userTO.getResources().add(RESOURCE_NAME_TIMEOUT);
 +        userTO = createUser(userTO);
 +        assertEquals(RESOURCE_NAME_TIMEOUT, userTO.getPropagationStatusTOs().get(0).getResource());
 +        assertNotNull(userTO.getPropagationStatusTOs().get(0).getFailureReason());
 +        assertEquals(PropagationTaskExecStatus.UNSUBMITTED, userTO.getPropagationStatusTOs().get(0).getStatus());
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE122() {
 +        // 1. create user on testdb and testdb2
 +        UserTO userTO = getUniqueSampleTO("syncope122@apache.org");
 +        userTO.getResources().clear();
 +
 +        userTO.getResources().add(RESOURCE_NAME_TESTDB);
 +        userTO.getResources().add(RESOURCE_NAME_TESTDB2);
 +
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +        assertTrue(userTO.getResources().contains(RESOURCE_NAME_TESTDB));
 +        assertTrue(userTO.getResources().contains(RESOURCE_NAME_TESTDB2));
 +
 +        final String pwdOnSyncope = userTO.getPassword();
 +
 +        ConnObjectTO userOnDb = resourceService.readConnObject(
 +                RESOURCE_NAME_TESTDB, AnyTypeKind.USER.name(), userTO.getKey());
 +        final AttrTO pwdOnTestDbAttr = userOnDb.getPlainAttrMap().get(OperationalAttributes.PASSWORD_NAME);
 +        assertNotNull(pwdOnTestDbAttr);
 +        assertNotNull(pwdOnTestDbAttr.getValues());
 +        assertFalse(pwdOnTestDbAttr.getValues().isEmpty());
 +        final String pwdOnTestDb = pwdOnTestDbAttr.getValues().iterator().next();
 +
 +        ConnObjectTO userOnDb2 = resourceService.readConnObject(
 +                RESOURCE_NAME_TESTDB2, AnyTypeKind.USER.name(), userTO.getKey());
 +        final AttrTO pwdOnTestDb2Attr = userOnDb2.getPlainAttrMap().get(OperationalAttributes.PASSWORD_NAME);
 +        assertNotNull(pwdOnTestDb2Attr);
 +        assertNotNull(pwdOnTestDb2Attr.getValues());
 +        assertFalse(pwdOnTestDb2Attr.getValues().isEmpty());
 +        final String pwdOnTestDb2 = pwdOnTestDb2Attr.getValues().iterator().next();
 +
 +        // 2. request to change password only on testdb (no Syncope, no testdb2)
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +        userMod.setPassword(getUUIDString());
 +        StatusMod pwdPropRequest = new StatusMod();
 +        pwdPropRequest.setOnSyncope(false);
-         pwdPropRequest.getResourceNames().add(RESOURCE_NAME_TESTDB);
++        pwdPropRequest.getResources().add(RESOURCE_NAME_TESTDB);
 +        userMod.setPwdPropRequest(pwdPropRequest);
 +
 +        userTO = updateUser(userMod);
 +
 +        // 3a. Chech that only a single propagation took place
 +        assertNotNull(userTO.getPropagationStatusTOs());
 +        assertEquals(1, userTO.getPropagationStatusTOs().size());
 +        assertEquals(RESOURCE_NAME_TESTDB, userTO.getPropagationStatusTOs().iterator().next().getResource());
 +
 +        // 3b. verify that password hasn't changed on Syncope
 +        assertEquals(pwdOnSyncope, userTO.getPassword());
 +
 +        // 3c. verify that password *has* changed on testdb
 +        userOnDb = resourceService.readConnObject(RESOURCE_NAME_TESTDB, AnyTypeKind.USER.name(), userTO.getKey());
 +        final AttrTO pwdOnTestDbAttrAfter = userOnDb.getPlainAttrMap().get(OperationalAttributes.PASSWORD_NAME);
 +        assertNotNull(pwdOnTestDbAttrAfter);
 +        assertNotNull(pwdOnTestDbAttrAfter.getValues());
 +        assertFalse(pwdOnTestDbAttrAfter.getValues().isEmpty());
 +        assertNotEquals(pwdOnTestDb, pwdOnTestDbAttrAfter.getValues().iterator().next());
 +
 +        // 3d. verify that password hasn't changed on testdb2
 +        userOnDb2 = resourceService.readConnObject(RESOURCE_NAME_TESTDB2, AnyTypeKind.USER.name(), userTO.getKey());
 +        final AttrTO pwdOnTestDb2AttrAfter = userOnDb2.getPlainAttrMap().get(OperationalAttributes.PASSWORD_NAME);
 +        assertNotNull(pwdOnTestDb2AttrAfter);
 +        assertNotNull(pwdOnTestDb2AttrAfter.getValues());
 +        assertFalse(pwdOnTestDb2AttrAfter.getValues().isEmpty());
 +        assertEquals(pwdOnTestDb2, pwdOnTestDb2AttrAfter.getValues().iterator().next());
 +    }
 +
 +    @Test
 +    public void isseSYNCOPE136AES() {
 +        // 1. read configured cipher algorithm in order to be able to restore it at the end of test
 +        AttrTO pwdCipherAlgo = configurationService.get("password.cipher.algorithm");
 +        final String origpwdCipherAlgo = pwdCipherAlgo.getValues().get(0);
 +
 +        // 2. set AES password cipher algorithm
 +        pwdCipherAlgo.getValues().set(0, "AES");
 +        configurationService.set(pwdCipherAlgo);
 +
-         // 3. create user with no resources
-         UserTO userTO = getUniqueSampleTO("syncope136_AES@apache.org");
-         userTO.getResources().clear();
- 
-         userTO = createUser(userTO);
-         assertNotNull(userTO);
- 
-         // 4. update user, assign a propagation primary resource but don't provide any password
-         UserMod userMod = new UserMod();
-         userMod.setKey(userTO.getKey());
-         userMod.getResourcesToAdd().add(RESOURCE_NAME_WS1);
++        try {
++            // 3. create user with no resources
++            UserTO userTO = getUniqueSampleTO("syncope136_AES@apache.org");
++            userTO.getResources().clear();
 +
-         final StatusMod st = new StatusMod();
-         st.setOnSyncope(false);
-         st.getResourceNames().add(RESOURCE_NAME_WS1);
-         userMod.setPwdPropRequest(st);
++            userTO = createUser(userTO);
++            assertNotNull(userTO);
 +
-         userTO = updateUser(userMod);
-         assertNotNull(userTO);
++            // 4. update user, assign a propagation primary resource but don't provide any password
++            UserMod userMod = new UserMod();
++            userMod.setKey(userTO.getKey());
++            userMod.getResourcesToAdd().add(RESOURCE_NAME_WS1);
 +
-         // 5. verify that propagation was successful
-         List<PropagationStatus> props = userTO.getPropagationStatusTOs();
-         assertNotNull(props);
-         assertEquals(1, props.size());
-         PropagationStatus prop = props.iterator().next();
-         assertNotNull(prop);
-         assertEquals(RESOURCE_NAME_WS1, prop.getResource());
-         assertEquals(PropagationTaskExecStatus.SUBMITTED, prop.getStatus());
++            StatusMod st = new StatusMod();
++            st.setOnSyncope(false);
++            st.getResources().add(RESOURCE_NAME_WS1);
++            userMod.setPwdPropRequest(st);
 +
-         // 6. restore initial cipher algorithm
-         pwdCipherAlgo.getValues().set(0, origpwdCipherAlgo);
-         configurationService.set(pwdCipherAlgo);
++            userTO = updateUser(userMod);
++            assertNotNull(userTO);
++
++            // 5. verify that propagation was successful
++            List<PropagationStatus> props = userTO.getPropagationStatusTOs();
++            assertNotNull(props);
++            assertEquals(1, props.size());
++            PropagationStatus prop = props.iterator().next();
++            assertNotNull(prop);
++            assertEquals(RESOURCE_NAME_WS1, prop.getResource());
++            assertEquals(PropagationTaskExecStatus.SUBMITTED, prop.getStatus());
++        } finally {
++            // restore initial cipher algorithm
++            pwdCipherAlgo.getValues().set(0, origpwdCipherAlgo);
++            configurationService.set(pwdCipherAlgo);
++        }
 +    }
 +
 +    @Test
 +    public void isseSYNCOPE136Random() {
 +        // 1. create user with no resources
 +        UserTO userTO = getUniqueSampleTO("syncope136_Random@apache.org");
 +        userTO.getResources().clear();
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +
 +        // 2. update user, assign a propagation primary resource but don't provide any password
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +        userMod.getResourcesToAdd().add(RESOURCE_NAME_LDAP);
 +
-         final StatusMod st = new StatusMod();
++        StatusMod st = new StatusMod();
 +        st.setOnSyncope(false);
-         st.getResourceNames().add(RESOURCE_NAME_LDAP);
++        st.getResources().add(RESOURCE_NAME_LDAP);
 +        userMod.setPwdPropRequest(st);
 +
 +        userTO = updateUser(userMod);
 +        assertNotNull(userTO);
 +
 +        // 3. verify that propagation was successful
 +        List<PropagationStatus> props = userTO.getPropagationStatusTOs();
 +        assertNotNull(props);
 +        assertEquals(1, props.size());
 +        PropagationStatus prop = props.iterator().next();
 +        assertNotNull(prop);
 +        assertEquals(RESOURCE_NAME_LDAP, prop.getResource());
 +        assertEquals(PropagationTaskExecStatus.SUCCESS, prop.getStatus());
 +    }
 +
 +    @Test
 +    public void mappingPurpose() {
 +        UserTO userTO = getUniqueSampleTO("mpurpose@apache.org");
 +        userTO.getAuxClasses().add("csv");
 +
 +        AttrTO csvuserid = new AttrTO();
 +        csvuserid.setSchema("csvuserid");
 +        userTO.getDerAttrs().add(csvuserid);
 +
 +        userTO.getResources().clear();
 +        userTO.getResources().add(RESOURCE_NAME_CSV);
 +
 +        UserTO actual = createUser(userTO);
 +        assertNotNull(actual);
 +
 +        ConnObjectTO connObjectTO =
 +                resourceService.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), actual.getKey());
 +        assertNull(connObjectTO.getPlainAttrMap().get("email"));
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE265() {
 +        for (long key = 1; key <= 5; key++) {
 +            UserMod userMod = new UserMod();
 +            userMod.setKey(key);
 +
 +            userMod.getPlainAttrsToRemove().add("type");
 +            userMod.getPlainAttrsToUpdate().add(attrMod("type", "a type"));
 +
 +            UserTO userTO = updateUser(userMod);
 +
 +            assertEquals("a type", userTO.getPlainAttrMap().get("type").getValues().get(0));
 +        }
 +    }
 +
 +    @Test
 +    public void bulkActions() {
 +        BulkAction bulkAction = new BulkAction();
 +
 +        for (int i = 0; i < 10; i++) {
 +            UserTO userTO = getUniqueSampleTO("bulk_" + i + "@apache.org");
 +            bulkAction.getTargets().add(String.valueOf(createUser(userTO).getKey()));
 +        }
 +
 +        // check for a fail
 +        bulkAction.getTargets().add(String.valueOf(Long.MAX_VALUE));
 +
 +        assertEquals(11, bulkAction.getTargets().size());
 +
 +        bulkAction.setType(BulkAction.Type.SUSPEND);
 +        BulkActionResult res = userService.bulk(bulkAction);
 +        assertEquals(10, res.getResultByStatus(Status.SUCCESS).size());
 +        assertEquals(1, res.getResultByStatus(Status.FAILURE).size());
 +        assertEquals("suspended", userService.read(
 +                Long.parseLong(res.getResultByStatus(Status.SUCCESS).get(3))).getStatus());
 +
 +        bulkAction.setType(BulkAction.Type.REACTIVATE);
 +        res = userService.bulk(bulkAction);
 +        assertEquals(10, res.getResultByStatus(Status.SUCCESS).size());
 +        assertEquals(1, res.getResultByStatus(Status.FAILURE).size());
 +        assertEquals("active", userService.read(
 +                Long.parseLong(res.getResultByStatus(Status.SUCCESS).get(3))).getStatus());
 +
 +        bulkAction.setType(BulkAction.Type.DELETE);
 +        res = userService.bulk(bulkAction);
 +        assertEquals(10, res.getResultByStatus(Status.SUCCESS).size());
 +        assertEquals(1, res.getResultByStatus(Status.FAILURE).size());
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE354() {
 +        // change resource-ldap group mapping for including uniqueMember (need for assertions below)
 +        ResourceTO ldap = resourceService.read(RESOURCE_NAME_LDAP);
 +        for (MappingItemTO item : ldap.getProvision(AnyTypeKind.GROUP.name()).getMapping().getItems()) {
 +            if ("description".equals(item.getExtAttrName())) {
 +                item.setExtAttrName("uniqueMember");
 +            }
 +        }
 +        resourceService.update(ldap);
 +
 +        // 1. create group with LDAP resource
 +        GroupTO groupTO = new GroupTO();
 +        groupTO.setName("SYNCOPE354-" + getUUIDString());
 +        groupTO.setRealm("/");
 +        groupTO.getResources().add(RESOURCE_NAME_LDAP);
 +
 +        groupTO = createGroup(groupTO);
 +        assertNotNull(groupTO);
 +
 +        // 2. create user with LDAP resource and membership of the above group
 +        UserTO userTO = getUniqueSampleTO("syncope354@syncope.apache.org");
 +        userTO.getResources().add(RESOURCE_NAME_LDAP);
 +        MembershipTO membershipTO = new MembershipTO();
 +        membershipTO.setRightKey(groupTO.getKey());
 +        userTO.getMemberships().add(membershipTO);
 +
 +        userTO = createUser(userTO);
 +        assertTrue(userTO.getResources().contains(RESOURCE_NAME_LDAP));
 +
 +        // 3. read group on resource, check that user DN is included in uniqueMember
 +        ConnObjectTO connObj = resourceService.readConnObject(
 +                RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), groupTO.getKey());
 +        assertNotNull(connObj);
 +        assertTrue(connObj.getPlainAttrMap().get("uniqueMember").getValues().
 +                contains("uid=" + userTO.getUsername() + ",ou=people,o=isp"));
 +
 +        // 4. remove membership
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +        userMod.getMembershipsToRemove().add(userTO.getMemberships().get(0).getRightKey());
 +
 +        userTO = updateUser(userMod);
 +        assertTrue(userTO.getResources().contains(RESOURCE_NAME_LDAP));
 +
 +        // 5. read group on resource, check that user DN was removed from uniqueMember
 +        connObj = resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.GROUP.name(), groupTO.getKey());
 +        assertNotNull(connObj);
 +        assertFalse(connObj.getPlainAttrMap().get("uniqueMember").getValues().
 +                contains("uid=" + userTO.getUsername() + ",ou=people,o=isp"));
 +
 +        // 6. restore original resource-ldap group mapping
 +        for (MappingItemTO item : ldap.getProvision(AnyTypeKind.GROUP.name()).getMapping().getItems()) {
 +            if ("uniqueMember".equals(item.getExtAttrName())) {
 +                item.setExtAttrName("description");
 +            }
 +        }
 +        resourceService.update(ldap);
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE357() throws IOException {
 +        // 1. create group with LDAP resource
 +        GroupTO groupTO = new GroupTO();
 +        groupTO.setName("SYNCOPE357-" + getUUIDString());
 +        groupTO.setRealm("/");
 +        groupTO.getResources().add(RESOURCE_NAME_LDAP);
 +
 +        groupTO = createGroup(groupTO);
 +        assertNotNull(groupTO);
 +
 +        // 2. create user with membership of the above group
 +        UserTO userTO = getUniqueSampleTO("syncope357@syncope.apache.org");
 +        userTO.getPlainAttrs().add(attrTO("obscure", "valueToBeObscured"));
 +        userTO.getPlainAttrs().add(attrTO("photo",
 +                Base64Utility.encode(IOUtils.readBytesFromStream(getClass().getResourceAsStream("/favicon.jpg")))));
 +        MembershipTO membershipTO = new MembershipTO();
 +        membershipTO.setRightKey(groupTO.getKey());
 +        userTO.getMemberships().add(membershipTO);
 +
 +        userTO = createUser(userTO);
 +        assertTrue(userTO.getResources().contains(RESOURCE_NAME_LDAP));
 +        assertNotNull(userTO.getPlainAttrMap().get("obscure"));
 +        assertNotNull(userTO.getPlainAttrMap().get("photo"));
 +
 +        // 3. read user on resource
 +        ConnObjectTO connObj = resourceService.readConnObject(
 +                RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), userTO.getKey());
 +        assertNotNull(connObj);
 +        AttrTO registeredAddress = connObj.getPlainAttrMap().get("registeredAddress");
 +        assertNotNull(registeredAddress);
 +        assertEquals(userTO.getPlainAttrMap().get("obscure").getValues(), registeredAddress.getValues());
 +        AttrTO jpegPhoto = connObj.getPlainAttrMap().get("jpegPhoto");
 +        assertNotNull(jpegPhoto);
 +        assertEquals(userTO.getPlainAttrMap().get("photo").getValues(), jpegPhoto.getValues());
 +
 +        // 4. remove group
 +        groupService.delete(groupTO.getKey());
 +
 +        // 5. try to read user on resource: fail
 +        try {
 +            resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), userTO.getKey());
 +            fail();
 +        } catch (SyncopeClientException e) {
 +            assertEquals(ClientExceptionType.NotFound, e.getType());
 +        }
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE383() {
 +        // 1. create user without resources
 +        UserTO userTO = getUniqueSampleTO("syncope383@apache.org");
 +        userTO.getResources().clear();
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +
 +        // 2. assign resource without specifying a new pwd and check propagation failure
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +        userMod.getResourcesToAdd().add(RESOURCE_NAME_TESTDB);
 +        userTO = updateUser(userMod);
 +        assertEquals(RESOURCE_NAME_TESTDB, userTO.getResources().iterator().next());
 +        assertFalse(userTO.getPropagationStatusTOs().get(0).getStatus().isSuccessful());
 +        assertNotNull(userTO.getPropagationStatusTOs().get(0).getFailureReason());
 +
 +        // 3. request to change password only on testdb
 +        userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +        userMod.setPassword(getUUIDString() + "abbcbcbddd123");
 +        StatusMod pwdPropRequest = new StatusMod();
-         pwdPropRequest.getResourceNames().add(RESOURCE_NAME_TESTDB);
++        pwdPropRequest.getResources().add(RESOURCE_NAME_TESTDB);
 +        userMod.setPwdPropRequest(pwdPropRequest);
 +
 +        userTO = updateUser(userMod);
 +        assertEquals(RESOURCE_NAME_TESTDB, userTO.getResources().iterator().next());
 +        assertTrue(userTO.getPropagationStatusTOs().get(0).getStatus().isSuccessful());
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE402() {
 +        // 1. create an user with strict mandatory attributes only
 +        UserTO userTO = new UserTO();
 +        userTO.setRealm(SyncopeConstants.ROOT_REALM);
 +        String userId = getUUIDString() + "syncope402@syncope.apache.org";
 +        userTO.setUsername(userId);
 +        userTO.setPassword("password123");
 +
 +        userTO.getPlainAttrs().add(attrTO("userId", userId));
 +        userTO.getPlainAttrs().add(attrTO("fullname", userId));
 +        userTO.getPlainAttrs().add(attrTO("surname", userId));
 +
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +        assertTrue(userTO.getResources().isEmpty());
 +
 +        // 2. update assigning a resource NOT forcing mandatory constraints
 +        // AND primary: must fail with PropagationException
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +        userMod.setPassword("newPassword123");
 +
 +        userMod.getResourcesToAdd().add(RESOURCE_NAME_WS1);
 +        userMod.getResourcesToAdd().add(RESOURCE_NAME_TESTDB);
 +        userTO = updateUser(userMod);
 +
 +        List<PropagationStatus> propagationStatuses = userTO.getPropagationStatusTOs();
 +        PropagationStatus ws1PropagationStatus = null;
 +        if (propagationStatuses != null) {
 +            for (PropagationStatus propStatus : propagationStatuses) {
 +                if (RESOURCE_NAME_WS1.equals(propStatus.getResource())) {
 +                    ws1PropagationStatus = propStatus;
 +                    break;
 +                }
 +            }
 +        }
 +        assertNotNull(ws1PropagationStatus);
 +        assertEquals(RESOURCE_NAME_WS1, ws1PropagationStatus.getResource());
 +        assertNotNull(ws1PropagationStatus.getFailureReason());
 +        assertEquals(PropagationTaskExecStatus.UNSUBMITTED, ws1PropagationStatus.getStatus());
 +    }
 +
 +    @Test
 +    public void unlink() {
 +        UserTO userTO = getUniqueSampleTO("unlink@syncope.apache.org");
 +        userTO.getResources().clear();
 +        userTO.getMemberships().clear();
 +        userTO.getDerAttrs().clear();
 +        userTO.getVirAttrs().clear();
 +        userTO.getAuxClasses().add("csv");
 +        userTO.getDerAttrs().add(attrTO("csvuserid", null));
 +        userTO.getResources().add(RESOURCE_NAME_CSV);
 +
 +        UserTO actual = createUser(userTO);
 +        assertNotNull(actual);
 +        assertNotNull(resourceService.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), actual.getKey()));
 +
 +        assertNotNull(userService.deassociate(actual.getKey(),
 +                ResourceDeassociationActionType.UNLINK,
 +                CollectionWrapper.wrap(RESOURCE_NAME_CSV, ResourceKey.class)).
 +                readEntity(BulkActionResult.class));
 +
 +        actual = userService.read(actual.getKey());
 +        assertNotNull(actual);
 +        assertTrue(actual.getResources().isEmpty());
 +
 +        assertNotNull(resourceService.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), actual.getKey()));
 +    }
 +
 +    @Test
 +    public void link() {
 +        UserTO userTO = getUniqueSampleTO("link@syncope.apache.org");
 +        userTO.getResources().clear();
 +        userTO.getMemberships().clear();
 +        userTO.getDerAttrs().clear();
 +        userTO.getVirAttrs().clear();
 +        userTO.getAuxClasses().add("csv");
 +        userTO.getDerAttrs().add(attrTO("csvuserid", null));
 +
 +        UserTO actual = createUser(userTO);
 +        assertNotNull(actual);
 +        assertTrue(actual.getResources().isEmpty());
 +
 +        try {
 +            resourceService.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), actual.getKey());
 +            fail();
 +        } catch (Exception e) {
 +            assertNotNull(e);
 +        }
 +
 +        final ResourceAssociationMod associationMod = new ResourceAssociationMod();
 +        associationMod.getTargetResources().addAll(CollectionWrapper.wrap(RESOURCE_NAME_CSV, ResourceKey.class));
 +
 +        assertNotNull(userService.associate(actual.getKey(), ResourceAssociationAction.LINK, associationMod).readEntity(
 +                BulkActionResult.class));
 +
 +        actual = userService.read(actual.getKey());
 +        assertNotNull(actual);
 +        assertFalse(actual.getResources().isEmpty());
 +
 +        try {
 +            resourceService.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), actual.getKey());
 +            fail();
 +        } catch (Exception e) {
 +            assertNotNull(e);
 +        }
 +    }
 +
 +    @Test
 +    public void unassign() {
 +        UserTO userTO = getUniqueSampleTO("unassign@syncope.apache.org");
 +        userTO.getResources().clear();
 +        userTO.getMemberships().clear();
 +        userTO.getDerAttrs().clear();
 +        userTO.getVirAttrs().clear();
 +        userTO.getAuxClasses().add("csv");
 +        userTO.getDerAttrs().add(attrTO("csvuserid", null));
 +        userTO.getResources().add(RESOURCE_NAME_CSV);
 +
 +        UserTO actual = createUser(userTO);
 +        assertNotNull(actual);
 +        assertNotNull(resourceService.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), actual.getKey()));
 +
 +        assertNotNull(userService.deassociate(actual.getKey(),
 +                ResourceDeassociationActionType.UNASSIGN,
 +                CollectionWrapper.wrap(RESOURCE_NAME_CSV, ResourceKey.class)).
 +                readEntity(BulkActionResult.class));
 +
 +        actual = userService.read(actual.getKey());
 +        assertNotNull(actual);
 +        assertTrue(actual.getResources().isEmpty());
 +
 +        try {
 +            resourceService.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), actual.getKey());
 +            fail();
 +        } catch (Exception e) {
 +            assertNotNull(e);
 +        }
 +    }
 +
 +    @Test
 +    public void assign() {
 +        UserTO userTO = getUniqueSampleTO("assign@syncope.apache.org");
 +        userTO.getResources().clear();
 +        userTO.getMemberships().clear();
 +        userTO.getDerAttrs().clear();
 +        userTO.getVirAttrs().clear();
 +        userTO.getAuxClasses().add("csv");
 +        userTO.getDerAttrs().add(attrTO("csvuserid", null));
 +
 +        UserTO actual = createUser(userTO);
 +        assertNotNull(actual);
 +        assertTrue(actual.getResources().isEmpty());
 +
 +        try {
 +            resourceService.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), actual.getKey());
 +            fail();
 +        } catch (Exception e) {
 +            assertNotNull(e);
 +        }
 +
 +        ResourceAssociationMod associationMod = new ResourceAssociationMod();
 +        associationMod.getTargetResources().addAll(CollectionWrapper.wrap(RESOURCE_NAME_CSV, ResourceKey.class));
 +        associationMod.setChangePwd(true);
 +        associationMod.setPassword("password");
 +
 +        assertNotNull(userService.associate(actual.getKey(), ResourceAssociationAction.ASSIGN, associationMod)
 +                .readEntity(BulkActionResult.class));
 +
 +        actual = userService.read(actual.getKey());
 +        assertNotNull(actual);
 +        assertFalse(actual.getResources().isEmpty());
 +        assertNotNull(resourceService.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), actual.getKey()));
 +    }
 +
 +    @Test
 +    public void deprovision() {
 +        UserTO userTO = getUniqueSampleTO("deprovision@syncope.apache.org");
 +        userTO.getResources().clear();
 +        userTO.getMemberships().clear();
 +        userTO.getDerAttrs().clear();
 +        userTO.getVirAttrs().clear();
 +        userTO.getAuxClasses().add("csv");
 +        userTO.getDerAttrs().add(attrTO("csvuserid", null));
 +        userTO.getResources().add(RESOURCE_NAME_CSV);
 +
 +        UserTO actual = createUser(userTO);
 +        assertNotNull(actual);
 +        assertNotNull(resourceService.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), actual.getKey()));
 +
 +        assertNotNull(userService.deassociate(actual.getKey(),
 +                ResourceDeassociationActionType.DEPROVISION,
 +                CollectionWrapper.wrap(RESOURCE_NAME_CSV, ResourceKey.class)).
 +                readEntity(BulkActionResult.class));
 +
 +        actual = userService.read(actual.getKey());
 +        assertNotNull(actual);
 +        assertFalse(actual.getResources().isEmpty());
 +
 +        try {
 +            resourceService.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), actual.getKey());
 +            fail();
 +        } catch (SyncopeClientException e) {
 +            assertEquals(ClientExceptionType.NotFound, e.getType());
 +        }
 +    }
 +
 +    @Test
 +    public void provision() {
 +        UserTO userTO = getUniqueSampleTO("provision@syncope.apache.org");
 +        userTO.getResources().clear();
 +        userTO.getMemberships().clear();
 +        userTO.getDerAttrs().clear();
 +        userTO.getVirAttrs().clear();
 +        userTO.getAuxClasses().add("csv");
 +        userTO.getDerAttrs().add(attrTO("csvuserid", null));
 +
 +        UserTO actual = createUser(userTO);
 +        assertNotNull(actual);
 +        assertTrue(actual.getResources().isEmpty());
 +
 +        try {
 +            resourceService.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), actual.getKey());
 +            fail();
 +        } catch (Exception e) {
 +            assertNotNull(e);
 +        }
 +
 +        ResourceAssociationMod associationMod = new ResourceAssociationMod();
 +        associationMod.getTargetResources().addAll(CollectionWrapper.wrap(RESOURCE_NAME_CSV, ResourceKey.class));
 +        associationMod.setChangePwd(true);
 +        associationMod.setPassword("password");
 +
 +        assertNotNull(userService.associate(actual.getKey(), ResourceAssociationAction.PROVISION, associationMod).
 +                readEntity(BulkActionResult.class));
 +
 +        actual = userService.read(actual.getKey());
 +        assertNotNull(actual);
 +        assertTrue(actual.getResources().isEmpty());
 +        assertNotNull(resourceService.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), actual.getKey()));
 +    }
 +
 +    @Test
 +    public void deprovisionUnlinked() {
 +        UserTO userTO = getUniqueSampleTO("provision@syncope.apache.org");
 +        userTO.getResources().clear();
 +        userTO.getMemberships().clear();
 +        userTO.getDerAttrs().clear();
 +        userTO.getVirAttrs().clear();
 +        userTO.getAuxClasses().add("csv");
 +        userTO.getDerAttrs().add(attrTO("csvuserid", null));
 +
 +        UserTO actual = createUser(userTO);
 +        assertNotNull(actual);
 +        assertTrue(actual.getResources().isEmpty());
 +
 +        try {
 +            resourceService.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), actual.getKey());
 +            fail();
 +        } catch (Exception e) {
 +            assertNotNull(e);
 +        }
 +
 +        final ResourceAssociationMod associationMod = new ResourceAssociationMod();
 +        associationMod.getTargetResources().addAll(CollectionWrapper.wrap(RESOURCE_NAME_CSV, ResourceKey.class));
 +        associationMod.setChangePwd(true);
 +        associationMod.setPassword("password");
 +
 +        assertNotNull(userService.associate(actual.getKey(), ResourceAssociationAction.PROVISION,
 +                associationMod)
 +                .readEntity(BulkActionResult.class));
 +
 +        actual = userService.read(actual.getKey());
 +        assertNotNull(actual);
 +        assertTrue(actual.getResources().isEmpty());
 +        assertNotNull(resourceService.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), actual.getKey()));
 +
 +        assertNotNull(userService.deassociate(actual.getKey(),
 +                ResourceDeassociationActionType.DEPROVISION,
 +                CollectionWrapper.wrap(RESOURCE_NAME_CSV, ResourceKey.class)).
 +                readEntity(BulkActionResult.class));
 +
 +        actual = userService.read(actual.getKey());
 +        assertNotNull(actual);
 +        assertTrue(actual.getResources().isEmpty());
 +
 +        try {
 +            resourceService.readConnObject(RESOURCE_NAME_CSV, AnyTypeKind.USER.name(), actual.getKey());
 +            fail();
 +        } catch (Exception e) {
 +            assertNotNull(e);
 +        }
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE420() {
 +        UserTO userTO = getUniqueSampleTO("syncope420@syncope.apache.org");
 +        userTO.getPlainAttrs().add(attrTO("makeItDouble", "3"));
 +
 +        userTO = createUser(userTO);
 +        assertEquals("6", userTO.getPlainAttrMap().get("makeItDouble").getValues().get(0));
 +
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +        userMod.getPlainAttrsToRemove().add("makeItDouble");
 +        userMod.getPlainAttrsToUpdate().add(attrMod("makeItDouble", "7"));
 +
 +        userTO = updateUser(userMod);
 +        assertEquals("14", userTO.getPlainAttrMap().get("makeItDouble").getValues().get(0));
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE426() {
 +        UserTO userTO = getUniqueSampleTO("syncope426@syncope.apache.org");
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +        userMod.setPassword("anotherPassword123");
 +        userTO = userService.update(userMod).readEntity(UserTO.class);
 +        assertNotNull(userTO);
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE435() {
 +        // 1. create user without password
 +        UserTO userTO = getUniqueSampleTO("syncope435@syncope.apache.org");
 +        userTO.setPassword(null);
 +        userTO = createUser(userTO, false);
 +        assertNotNull(userTO);
 +
 +        // 2. try to update user by subscribing a resource - works

<TRUNCATED>

[27/33] syncope git commit: [SYNCOPE-686] Merge from 1_2_X

Posted by md...@apache.org.
http://git-wip-us.apache.org/repos/asf/syncope/blob/054ea9ca/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserStatusPropagationProcessor.java
----------------------------------------------------------------------
diff --cc ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserStatusPropagationProcessor.java
index cbc8fb7,0000000..41af6eb
mode 100644,000000..100644
--- a/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserStatusPropagationProcessor.java
+++ b/ext/camel/provisioning-camel/src/main/java/org/apache/syncope/core/provisioning/camel/processor/UserStatusPropagationProcessor.java
@@@ -1,79 -1,0 +1,79 @@@
 +/*
 + * 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.provisioning.camel.processor;
 +
 +import java.util.Collection;
 +import java.util.List;
 +import org.apache.camel.Exchange;
 +import org.apache.camel.Processor;
 +import org.apache.commons.collections4.CollectionUtils;
 +import org.apache.commons.lang3.tuple.ImmutablePair;
 +import org.apache.syncope.common.lib.mod.StatusMod;
 +import org.apache.syncope.core.misc.spring.ApplicationContextProvider;
 +import org.apache.syncope.core.persistence.api.dao.UserDAO;
 +import org.apache.syncope.core.persistence.api.entity.task.PropagationTask;
 +import org.apache.syncope.core.provisioning.api.WorkflowResult;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationException;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationReporter;
 +import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +import org.springframework.beans.factory.annotation.Autowired;
 +import org.springframework.stereotype.Component;
 +
 +@Component
 +public class UserStatusPropagationProcessor implements Processor {
 +
 +    private static final Logger LOG = LoggerFactory.getLogger(UserStatusPropagationProcessor.class);
 +
 +    @Autowired
 +    protected PropagationManager propagationManager;
 +
 +    @Autowired
 +    protected PropagationTaskExecutor taskExecutor;
 +
 +    @Autowired
 +    protected UserDAO userDAO;
 +
 +    @SuppressWarnings("unchecked")
 +    @Override
 +    public void process(final Exchange exchange) {
 +        WorkflowResult<Long> updated = (WorkflowResult) exchange.getIn().getBody();
 +
 +        Long key = exchange.getProperty("userKey", Long.class);
 +        StatusMod statusMod = exchange.getProperty("statusMod", StatusMod.class);
 +
 +        Collection<String> resourcesToBeExcluded = CollectionUtils.removeAll(
-                 userDAO.findAllResourceNames(userDAO.find(key)), statusMod.getResourceNames());
++                userDAO.findAllResourceNames(userDAO.find(key)), statusMod.getResources());
 +
 +        List<PropagationTask> tasks = propagationManager.getUserUpdateTasks(
 +                key, statusMod.getType() != StatusMod.ModType.SUSPEND, resourcesToBeExcluded);
 +        PropagationReporter propReporter =
 +                ApplicationContextProvider.getBeanFactory().getBean(PropagationReporter.class);
 +        try {
 +            taskExecutor.execute(tasks, propReporter);
 +        } catch (PropagationException e) {
 +            LOG.error("Error propagation primary resource", e);
 +            propReporter.onPrimaryResourceFailure(tasks);
 +        }
 +
 +        exchange.getOut().setBody(new ImmutablePair<>(updated.getResult(), propReporter.getStatuses()));
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/syncope/blob/054ea9ca/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java
----------------------------------------------------------------------
diff --cc fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java
index 324a10d,0000000..f048c3f
mode 100644,000000..100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java
@@@ -1,770 -1,0 +1,770 @@@
 +/*
 + * 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.fit.core.reference;
 +
 +import static org.junit.Assert.assertEquals;
 +import static org.junit.Assert.assertFalse;
 +import static org.junit.Assert.assertNotNull;
 +import static org.junit.Assert.assertNull;
 +import static org.junit.Assert.assertTrue;
 +import static org.junit.Assert.fail;
 +
 +import java.util.HashSet;
 +import java.util.List;
 +import java.util.Locale;
 +import java.util.Set;
 +import javax.ws.rs.core.Response;
 +import org.apache.commons.collections4.CollectionUtils;
 +import org.apache.commons.collections4.Predicate;
 +import org.apache.syncope.client.lib.SyncopeClient;
 +import org.apache.syncope.common.lib.SyncopeClientException;
 +import org.apache.syncope.common.lib.SyncopeConstants;
 +import org.apache.syncope.common.lib.mod.StatusMod;
 +import org.apache.syncope.common.lib.mod.UserMod;
 +import org.apache.syncope.common.lib.to.AbstractTaskTO;
 +import org.apache.syncope.common.lib.to.AnyObjectTO;
 +import org.apache.syncope.common.lib.to.AttrTO;
 +import org.apache.syncope.common.lib.to.ConnInstanceTO;
 +import org.apache.syncope.common.lib.to.ConnObjectTO;
 +import org.apache.syncope.common.lib.to.MembershipTO;
 +import org.apache.syncope.common.lib.to.PagedResult;
 +import org.apache.syncope.common.lib.to.ResourceTO;
 +import org.apache.syncope.common.lib.to.GroupTO;
 +import org.apache.syncope.common.lib.to.ProvisionTO;
 +import org.apache.syncope.common.lib.policy.SyncPolicyTO;
 +import org.apache.syncope.common.lib.to.SyncTaskTO;
 +import org.apache.syncope.common.lib.to.TaskExecTO;
 +import org.apache.syncope.common.lib.to.UserTO;
 +import org.apache.syncope.common.lib.types.AnyTypeKind;
 +import org.apache.syncope.common.lib.types.CipherAlgorithm;
 +import org.apache.syncope.common.lib.types.ConnConfProperty;
 +import org.apache.syncope.common.lib.types.PropagationTaskExecStatus;
 +import org.apache.syncope.common.lib.types.ResourceDeassociationActionType;
 +import org.apache.syncope.common.lib.types.TaskType;
 +import org.apache.syncope.common.lib.wrap.ResourceKey;
 +import org.apache.syncope.common.rest.api.CollectionWrapper;
 +import org.apache.syncope.common.rest.api.service.TaskService;
 +import org.apache.syncope.core.misc.security.Encryptor;
 +import org.apache.syncope.core.provisioning.java.sync.DBPasswordSyncActions;
 +import org.apache.syncope.core.provisioning.java.sync.LDAPPasswordSyncActions;
 +import org.identityconnectors.framework.common.objects.Name;
 +import org.junit.BeforeClass;
 +import org.junit.FixMethodOrder;
 +import org.junit.Test;
 +import org.junit.runners.MethodSorters;
 +import org.springframework.dao.EmptyResultDataAccessException;
 +import org.springframework.jdbc.core.JdbcTemplate;
 +
 +@FixMethodOrder(MethodSorters.JVM)
 +public class SyncTaskITCase extends AbstractTaskITCase {
 +
 +    @BeforeClass
 +    public static void testSyncActionsSetup() {
 +        SyncTaskTO syncTask = taskService.read(SYNC_TASK_ID);
 +        syncTask.getActionsClassNames().add(TestSyncActions.class.getName());
 +        taskService.update(syncTask);
 +    }
 +
 +    @Test
 +    public void getSyncActionsClasses() {
 +        List<String> actions = syncopeService.info().getSyncActions();
 +        assertNotNull(actions);
 +        assertFalse(actions.isEmpty());
 +    }
 +
 +    @Test
 +    public void list() {
 +        PagedResult<SyncTaskTO> tasks =
 +                taskService.list(TaskType.SYNCHRONIZATION, SyncopeClient.getListQueryBuilder().build());
 +        assertFalse(tasks.getResult().isEmpty());
 +        for (AbstractTaskTO task : tasks.getResult()) {
 +            if (!(task instanceof SyncTaskTO)) {
 +                fail();
 +            }
 +        }
 +    }
 +
 +    @Test
 +    public void create() {
 +        SyncTaskTO task = new SyncTaskTO();
 +        task.setName("Test create Sync");
 +        task.setDestinationRealm("/");
 +        task.setResource(RESOURCE_NAME_WS2);
 +
 +        UserTO userTemplate = new UserTO();
 +        userTemplate.getResources().add(RESOURCE_NAME_WS2);
 +
 +        MembershipTO membershipTO = new MembershipTO();
 +        membershipTO.setRightKey(8L);
 +        userTemplate.getMemberships().add(membershipTO);
 +        task.getTemplates().put(AnyTypeKind.USER.name(), userTemplate);
 +
 +        GroupTO groupTemplate = new GroupTO();
 +        groupTemplate.getResources().add(RESOURCE_NAME_LDAP);
 +        task.getTemplates().put(AnyTypeKind.GROUP.name(), groupTemplate);
 +
 +        Response response = taskService.create(task);
 +        SyncTaskTO actual = getObject(response.getLocation(), TaskService.class, SyncTaskTO.class);
 +        assertNotNull(actual);
 +
 +        task = taskService.read(actual.getKey());
 +        assertNotNull(task);
 +        assertEquals(actual.getKey(), task.getKey());
 +        assertEquals(actual.getJobDelegateClassName(), task.getJobDelegateClassName());
 +        assertEquals(userTemplate, task.getTemplates().get(AnyTypeKind.USER.name()));
 +        assertEquals(groupTemplate, task.getTemplates().get(AnyTypeKind.GROUP.name()));
 +    }
 +
 +    @Test
 +    public void sync() throws Exception {
 +        removeTestUsers();
 +
 +        // -----------------------------
 +        // Create a new user ... it should be updated applying sync policy
 +        // -----------------------------
 +        UserTO inUserTO = new UserTO();
 +        inUserTO.setRealm(SyncopeConstants.ROOT_REALM);
 +        inUserTO.setPassword("password123");
 +        String userName = "test9";
 +        inUserTO.setUsername(userName);
 +        inUserTO.getPlainAttrs().add(attrTO("firstname", "nome9"));
 +        inUserTO.getPlainAttrs().add(attrTO("surname", "cognome"));
 +        inUserTO.getPlainAttrs().add(attrTO("type", "a type"));
 +        inUserTO.getPlainAttrs().add(attrTO("fullname", "nome cognome"));
 +        inUserTO.getPlainAttrs().add(attrTO("userId", "puccini@syncope.apache.org"));
 +        inUserTO.getPlainAttrs().add(attrTO("email", "puccini@syncope.apache.org"));
 +        inUserTO.getAuxClasses().add("csv");
 +        inUserTO.getDerAttrs().add(attrTO("csvuserid", null));
 +
 +        inUserTO = createUser(inUserTO);
 +        assertNotNull(inUserTO);
 +        assertFalse(inUserTO.getResources().contains(RESOURCE_NAME_CSV));
 +
 +        // -----------------------------
 +        try {
 +            int usersPre = userService.list(
 +                    SyncopeClient.getAnyListQueryBuilder().realm(SyncopeConstants.ROOT_REALM).
 +                    page(1).size(1).build()).getTotalCount();
 +            assertNotNull(usersPre);
 +
 +            execProvisioningTask(taskService, SYNC_TASK_ID, 50, false);
 +
 +            // after execution of the sync task the user data should have been synced from CSV
 +            // and processed by user template
 +            UserTO userTO = userService.read(inUserTO.getKey());
 +            assertNotNull(userTO);
 +            assertEquals(userName, userTO.getUsername());
 +            assertEquals(ActivitiDetector.isActivitiEnabledForUsers(syncopeService)
 +                    ? "active" : "created", userTO.getStatus());
 +            assertEquals("test9@syncope.apache.org", userTO.getPlainAttrMap().get("email").getValues().get(0));
 +            assertEquals("test9@syncope.apache.org", userTO.getPlainAttrMap().get("userId").getValues().get(0));
 +            assertTrue(Integer.valueOf(userTO.getPlainAttrMap().get("fullname").getValues().get(0)) <= 10);
 +            assertTrue(userTO.getResources().contains(RESOURCE_NAME_TESTDB));
 +            assertTrue(userTO.getResources().contains(RESOURCE_NAME_WS2));
 +
 +            // Matching --> Update (no link)
 +            assertFalse(userTO.getResources().contains(RESOURCE_NAME_CSV));
 +
 +            // check for user template
 +            userTO = readUser("test7");
 +            assertNotNull(userTO);
 +            assertEquals("TYPE_OTHER", userTO.getPlainAttrMap().get("type").getValues().get(0));
 +            assertEquals(3, userTO.getResources().size());
 +            assertTrue(userTO.getResources().contains(RESOURCE_NAME_TESTDB));
 +            assertTrue(userTO.getResources().contains(RESOURCE_NAME_WS2));
 +            assertEquals(1, userTO.getMemberships().size());
 +            assertEquals(8, userTO.getMemberships().get(0).getRightKey());
 +
 +            // Unmatching --> Assign (link) - SYNCOPE-658
 +            assertTrue(userTO.getResources().contains(RESOURCE_NAME_CSV));
 +            assertEquals(1, CollectionUtils.countMatches(userTO.getDerAttrs(), new Predicate<AttrTO>() {
 +
 +                @Override
 +                public boolean evaluate(final AttrTO attributeTO) {
 +                    return "csvuserid".equals(attributeTO.getSchema());
 +                }
 +            }));
 +
 +            userTO = readUser("test8");
 +            assertNotNull(userTO);
 +            assertEquals("TYPE_8", userTO.getPlainAttrMap().get("type").getValues().get(0));
 +
 +            // Check for ignored user - SYNCOPE-663
 +            try {
 +                readUser("test2");
 +                fail();
 +            } catch (SyncopeClientException e) {
 +                assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
 +            }
 +
 +            // check for sync results
 +            int usersPost = userService.list(
 +                    SyncopeClient.getAnyListQueryBuilder().realm(SyncopeConstants.ROOT_REALM).
 +                    page(1).size(1).build()).getTotalCount();
 +            assertNotNull(usersPost);
 +            assertEquals(usersPre + 8, usersPost);
 +
 +            // Check for issue 215:
 +            // * expected disabled user test1
 +            // * expected enabled user test2
 +            userTO = readUser("test1");
 +            assertNotNull(userTO);
 +            assertEquals("suspended", userTO.getStatus());
 +
 +            userTO = readUser("test3");
 +            assertNotNull(userTO);
 +            assertEquals("active", userTO.getStatus());
 +
 +            Set<Long> otherSyncTaskKeys = new HashSet<>();
 +            otherSyncTaskKeys.add(25L);
 +            otherSyncTaskKeys.add(26L);
 +            execProvisioningTasks(taskService, otherSyncTaskKeys, 50, false);
 +
 +            // Matching --> UNLINK
 +            assertFalse(readUser("test9").getResources().contains(RESOURCE_NAME_CSV));
 +            assertFalse(readUser("test7").getResources().contains(RESOURCE_NAME_CSV));
 +        } finally {
 +            removeTestUsers();
 +        }
 +    }
 +
 +    @Test
 +    public void dryRun() {
 +        TaskExecTO execution = execProvisioningTask(taskService, SYNC_TASK_ID, 50, true);
 +        assertEquals("Execution of task " + execution.getTask() + " failed with message " + execution.getMessage(),
 +                "SUCCESS", execution.getStatus());
 +    }
 +
 +    @Test
 +    public void reconcileFromDB() {
 +        // update sync task
 +        TaskExecTO execution = execProvisioningTask(taskService, 7L, 50, false);
 +        assertNotNull(execution.getStatus());
 +        assertTrue(PropagationTaskExecStatus.valueOf(execution.getStatus()).isSuccessful());
 +
 +        UserTO userTO = readUser("testuser1");
 +        assertNotNull(userTO);
 +        assertEquals("reconciled@syncope.apache.org", userTO.getPlainAttrMap().get("userId").getValues().get(0));
 +        assertEquals("suspended", userTO.getStatus());
 +
 +        // enable user on external resource
 +        JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
 +        jdbcTemplate.execute("UPDATE TEST SET STATUS=TRUE");
 +
 +        // re-execute the same SyncTask: now user must be active
 +        execution = execProvisioningTask(taskService, 7L, 50, false);
 +        assertNotNull(execution.getStatus());
 +        assertTrue(PropagationTaskExecStatus.valueOf(execution.getStatus()).isSuccessful());
 +
 +        userTO = readUser("testuser1");
 +        assertNotNull(userTO);
 +        assertEquals("active", userTO.getStatus());
 +    }
 +
 +    /**
 +     * Clean Syncope and LDAP resource status.
 +     */
 +    private void ldapCleanup() {
 +        PagedResult<GroupTO> matchingGroups = groupService.search(
 +                SyncopeClient.getAnySearchQueryBuilder().realm(SyncopeConstants.ROOT_REALM).
 +                fiql(SyncopeClient.getGroupSearchConditionBuilder().is("name").equalTo("testLDAPGroup").query()).
 +                build());
 +        if (matchingGroups.getSize() > 0) {
 +            for (GroupTO group : matchingGroups.getResult()) {
 +                groupService.deassociate(group.getKey(),
 +                        ResourceDeassociationActionType.UNLINK,
 +                        CollectionWrapper.wrap(RESOURCE_NAME_LDAP, ResourceKey.class));
 +                groupService.delete(group.getKey());
 +            }
 +        }
 +        PagedResult<UserTO> matchingUsers = userService.search(
 +                SyncopeClient.getAnySearchQueryBuilder().realm(SyncopeConstants.ROOT_REALM).
 +                fiql(SyncopeClient.getUserSearchConditionBuilder().is("username").equalTo("syncFromLDAP").query()).
 +                build());
 +        if (matchingUsers.getSize() > 0) {
 +            for (UserTO user : matchingUsers.getResult()) {
 +                userService.deassociate(user.getKey(),
 +                        ResourceDeassociationActionType.UNLINK,
 +                        CollectionWrapper.wrap(RESOURCE_NAME_LDAP, ResourceKey.class));
 +                userService.delete(user.getKey());
 +            }
 +        }
 +    }
 +
 +    @Test
 +    public void reconcileFromLDAP() {
 +        // First of all, clear any potential conflict with existing user / group
 +        ldapCleanup();
 +
 +        // 0. synchronize
 +        TaskExecTO execution = execProvisioningTask(taskService, 11L, 50, false);
 +
 +        // 1. verify execution status
 +        String status = execution.getStatus();
 +        assertNotNull(status);
 +        assertTrue(PropagationTaskExecStatus.valueOf(status).isSuccessful());
 +
 +        // 2. verify that synchronized group is found
 +        PagedResult<GroupTO> matchingGroups = groupService.search(
 +                SyncopeClient.getAnySearchQueryBuilder().realm(SyncopeConstants.ROOT_REALM).
 +                fiql(SyncopeClient.getGroupSearchConditionBuilder().is("name").equalTo("testLDAPGroup").query()).
 +                build());
 +        assertNotNull(matchingGroups);
 +        assertEquals(1, matchingGroups.getResult().size());
 +
 +        // 3. verify that synchronized user is found
 +        PagedResult<UserTO> matchingUsers = userService.search(
 +                SyncopeClient.getAnySearchQueryBuilder().realm(SyncopeConstants.ROOT_REALM).
 +                fiql(SyncopeClient.getUserSearchConditionBuilder().is("username").equalTo("syncFromLDAP").query()).
 +                build());
 +        assertNotNull(matchingUsers);
 +        assertEquals(1, matchingUsers.getResult().size());
 +
 +        // Check for SYNCOPE-436
 +        assertEquals("syncFromLDAP",
 +                matchingUsers.getResult().get(0).getVirAttrMap().get("virtualReadOnly").getValues().get(0));
 +        // Check for SYNCOPE-270
 +        assertNotNull(matchingUsers.getResult().get(0).getPlainAttrMap().get("obscure"));
 +        // Check for SYNCOPE-123
 +        assertNotNull(matchingUsers.getResult().get(0).getPlainAttrMap().get("photo"));
 +
 +        GroupTO groupTO = matchingGroups.getResult().iterator().next();
 +        assertNotNull(groupTO);
 +        assertEquals("testLDAPGroup", groupTO.getName());
 +        assertEquals("true", groupTO.getPlainAttrMap().get("show").getValues().get(0));
 +        assertEquals(matchingUsers.getResult().iterator().next().getKey(), groupTO.getUserOwner(), 0);
 +        assertNull(groupTO.getGroupOwner());
 +
 +        // SYNCOPE-317
 +        execProvisioningTask(taskService, 11L, 50, false);
 +    }
 +
 +    @Test
 +    public void reconcileFromScriptedSQL() {
 +        // 0. reset sync token
 +        ResourceTO resource = resourceService.read(RESOURCE_NAME_DBSCRIPTED);
 +        ProvisionTO provision = resource.getProvision("PRINTER");
 +        assertNotNull(provision);
 +
 +        provision.setSyncToken(null);
 +        resourceService.update(resource);
 +
 +        // 1. create printer on external resource
 +        AnyObjectTO anyObjectTO = AnyObjectITCase.getSampleTO("sync");
 +        anyObjectTO = createAnyObject(anyObjectTO);
 +        assertNotNull(anyObjectTO);
 +
 +        // 2. unlink any existing printer and delete from Syncope (printer is now only on external resource)
 +        PagedResult<AnyObjectTO> matchingPrinters = anyObjectService.search(
 +                SyncopeClient.getAnySearchQueryBuilder().realm(SyncopeConstants.ROOT_REALM).
 +                fiql(SyncopeClient.getAnyObjectSearchConditionBuilder().type("PRINTER").and().
 +                        is("location").equalTo("sync*").query()).build());
 +        assertTrue(matchingPrinters.getSize() > 0);
 +        for (AnyObjectTO printer : matchingPrinters.getResult()) {
 +            anyObjectService.deassociate(printer.getKey(),
 +                    ResourceDeassociationActionType.UNLINK,
 +                    CollectionWrapper.wrap(RESOURCE_NAME_DBSCRIPTED, ResourceKey.class));
 +            anyObjectService.delete(printer.getKey());
 +        }
 +
 +        // 3. synchronize
 +        execProvisioningTask(taskService, 28L, 50, false);
 +
 +        // 4. verify that printer was re-created in Syncope
 +        matchingPrinters = anyObjectService.search(
 +                SyncopeClient.getAnySearchQueryBuilder().realm(SyncopeConstants.ROOT_REALM).
 +                fiql(SyncopeClient.getAnyObjectSearchConditionBuilder().type("PRINTER").and().
 +                        is("location").equalTo("sync*").query()).build());
 +        assertTrue(matchingPrinters.getSize() > 0);
 +
 +        // 5. verify that synctoken was updated
 +        assertNotNull(
 +                resourceService.read(RESOURCE_NAME_DBSCRIPTED).getProvision(anyObjectTO.getType()).getSyncToken());
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE68() {
 +        //-----------------------------
 +        // Create a new user ... it should be updated applying sync policy
 +        //-----------------------------
 +        UserTO userTO = new UserTO();
 +        userTO.setRealm(SyncopeConstants.ROOT_REALM);
 +        userTO.setPassword("password123");
 +        userTO.setUsername("testuser2");
 +
 +        userTO.getPlainAttrs().add(attrTO("firstname", "testuser2"));
 +        userTO.getPlainAttrs().add(attrTO("surname", "testuser2"));
 +        userTO.getPlainAttrs().add(attrTO("type", "a type"));
 +        userTO.getPlainAttrs().add(attrTO("fullname", "a type"));
 +        userTO.getPlainAttrs().add(attrTO("userId", "testuser2@syncope.apache.org"));
 +        userTO.getPlainAttrs().add(attrTO("email", "testuser2@syncope.apache.org"));
 +
 +        userTO.getResources().add(RESOURCE_NAME_NOPROPAGATION2);
 +        userTO.getResources().add(RESOURCE_NAME_NOPROPAGATION4);
 +
 +        MembershipTO membershipTO = new MembershipTO();
 +        membershipTO.setRightKey(7L);
 +
 +        userTO.getMemberships().add(membershipTO);
 +
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +        assertEquals("testuser2", userTO.getUsername());
 +        assertEquals(1, userTO.getMemberships().size());
 +        assertEquals(3, userTO.getResources().size());
 +        //-----------------------------
 +
 +        try {
 +            //-----------------------------
 +            //  add user template
 +            //-----------------------------
 +            UserTO template = new UserTO();
 +
 +            membershipTO = new MembershipTO();
 +            membershipTO.setRightKey(10L);
 +
 +            template.getMemberships().add(membershipTO);
 +
 +            template.getResources().add(RESOURCE_NAME_NOPROPAGATION4);
 +            //-----------------------------
 +
 +            // Update sync task
 +            SyncTaskTO task = taskService.read(9L);
 +            assertNotNull(task);
 +
 +            task.getTemplates().put(AnyTypeKind.USER.name(), template);
 +
 +            taskService.update(task);
 +            SyncTaskTO actual = taskService.read(task.getKey());
 +            assertNotNull(actual);
 +            assertEquals(task.getKey(), actual.getKey());
 +            assertFalse(actual.getTemplates().get(AnyTypeKind.USER.name()).getResources().isEmpty());
 +            assertFalse(((UserTO) actual.getTemplates().get(AnyTypeKind.USER.name())).getMemberships().isEmpty());
 +
 +            TaskExecTO execution = execProvisioningTask(taskService, actual.getKey(), 50, false);
 +            final String status = execution.getStatus();
 +            assertNotNull(status);
 +            assertTrue(PropagationTaskExecStatus.valueOf(status).isSuccessful());
 +
 +            userTO = readUser("testuser2");
 +            assertNotNull(userTO);
 +            assertEquals("testuser2@syncope.apache.org", userTO.getPlainAttrMap().get("userId").getValues().get(0));
 +            assertEquals(2, userTO.getMemberships().size());
 +            assertEquals(4, userTO.getResources().size());
 +        } finally {
 +            UserTO dUserTO = deleteUser(userTO.getKey());
 +            assertNotNull(dUserTO);
 +        }
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE230() {
 +        // 1. read SyncTask for resource-db-sync (table TESTSYNC on external H2)
 +        execProvisioningTask(taskService, 10L, 50, false);
 +
 +        // 3. read e-mail address for user created by the SyncTask first execution
 +        UserTO userTO = readUser("issuesyncope230");
 +        assertNotNull(userTO);
 +        String email = userTO.getPlainAttrMap().get("email").getValues().iterator().next();
 +        assertNotNull(email);
 +
 +        // 4. update TESTSYNC on external H2 by changing e-mail address
 +        JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
 +        jdbcTemplate.execute("UPDATE TESTSYNC SET email='updatedSYNCOPE230@syncope.apache.org'");
 +
 +        // 5. re-execute the SyncTask
 +        execProvisioningTask(taskService, 10L, 50, false);
 +
 +        // 6. verify that the e-mail was updated
 +        userTO = readUser("issuesyncope230");
 +        assertNotNull(userTO);
 +        email = userTO.getPlainAttrMap().get("email").getValues().iterator().next();
 +        assertNotNull(email);
 +        assertEquals("updatedSYNCOPE230@syncope.apache.org", email);
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE258() {
 +        // -----------------------------
 +        // Add a custom correlation rule
 +        // -----------------------------
 +        SyncPolicyTO policyTO = policyService.read(9L);
 +        policyTO.getSpecification().getCorrelationRules().put(AnyTypeKind.USER.name(), TestSyncRule.class.getName());
 +        policyService.update(policyTO);
 +        // -----------------------------
 +
 +        SyncTaskTO task = new SyncTaskTO();
 +        task.setDestinationRealm(SyncopeConstants.ROOT_REALM);
 +        task.setName("Test Sync Rule");
 +        task.setResource(RESOURCE_NAME_WS2);
 +        task.setFullReconciliation(true);
 +        task.setPerformCreate(true);
 +        task.setPerformDelete(true);
 +        task.setPerformUpdate(true);
 +
 +        Response response = taskService.create(task);
 +        SyncTaskTO actual = getObject(response.getLocation(), TaskService.class, SyncTaskTO.class);
 +        assertNotNull(actual);
 +
 +        UserTO userTO = UserITCase.getUniqueSampleTO("s258_1@apache.org");
 +        userTO.getResources().clear();
 +        userTO.getResources().add(RESOURCE_NAME_WS2);
 +
 +        createUser(userTO);
 +
 +        userTO = UserITCase.getUniqueSampleTO("s258_2@apache.org");
 +        userTO.getResources().clear();
 +        userTO.getResources().add(RESOURCE_NAME_WS2);
 +
 +        userTO = createUser(userTO);
 +
 +        // change email in order to unmatch the second user
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(userTO.getKey());
 +        userMod.getPlainAttrsToRemove().add("email");
 +        userMod.getPlainAttrsToUpdate().add(attrMod("email", "s258@apache.org"));
 +
 +        userService.update(userMod);
 +
 +        execProvisioningTask(taskService, actual.getKey(), 50, false);
 +
 +        SyncTaskTO executed = taskService.read(actual.getKey());
 +        assertEquals(1, executed.getExecutions().size());
 +
 +        // asser for just one match
 +        assertTrue(executed.getExecutions().get(0).getMessage().substring(0, 55) + "...",
 +                executed.getExecutions().get(0).getMessage().contains("[updated/failures]: 1/0"));
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE272() {
 +        removeTestUsers();
 +
 +        // create user with testdb resource
 +        UserTO userTO = UserITCase.getUniqueSampleTO("syncope272@syncope.apache.org");
 +        userTO.getResources().add(RESOURCE_NAME_TESTDB);
 +
 +        userTO = createUser(userTO);
 +        try {
 +            assertNotNull(userTO);
 +            assertEquals(1, userTO.getPropagationStatusTOs().size());
 +            assertTrue(userTO.getPropagationStatusTOs().get(0).getStatus().isSuccessful());
 +
 +            TaskExecTO taskExecTO = execProvisioningTask(taskService, 24L, 50, false);
 +
 +            assertNotNull(taskExecTO.getStatus());
 +            assertTrue(PropagationTaskExecStatus.valueOf(taskExecTO.getStatus()).isSuccessful());
 +
 +            userTO = userService.read(userTO.getKey());
 +            assertNotNull(userTO);
 +            assertNotNull(userTO.getPlainAttrMap().get("firstname").getValues().get(0));
 +        } finally {
 +            removeTestUsers();
 +        }
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE307() {
 +        UserTO userTO = UserITCase.getUniqueSampleTO("s307@apache.org");
 +        userTO.getAuxClasses().add("csv");
 +
 +        AttrTO csvuserid = new AttrTO();
 +        csvuserid.setSchema("csvuserid");
 +        userTO.getDerAttrs().add(csvuserid);
 +
 +        userTO.getResources().clear();
 +        userTO.getResources().add(RESOURCE_NAME_WS2);
 +        userTO.getResources().add(RESOURCE_NAME_CSV);
 +
 +        userTO = createUser(userTO);
 +        assertNotNull(userTO);
 +
 +        userTO = userService.read(userTO.getKey());
 +        assertEquals("virtualvalue", userTO.getVirAttrMap().get("virtualdata").getValues().get(0));
 +
 +        // Update sync task
 +        SyncTaskTO task = taskService.read(12L);
 +        assertNotNull(task);
 +
 +        //  add user template
 +        UserTO template = new UserTO();
 +        template.getResources().add(RESOURCE_NAME_DBVIRATTR);
 +
 +        AttrTO userId = attrTO("userId", "'s307@apache.org'");
 +        template.getPlainAttrs().add(userId);
 +
 +        AttrTO email = attrTO("email", "'s307@apache.org'");
 +        template.getPlainAttrs().add(email);
 +
 +        task.getTemplates().put(AnyTypeKind.USER.name(), template);
 +
 +        taskService.update(task);
 +        execProvisioningTask(taskService, task.getKey(), 50, false);
 +
 +        // check for sync policy
 +        userTO = userService.read(userTO.getKey());
 +        assertEquals("virtualvalue", userTO.getVirAttrMap().get("virtualdata").getValues().get(0));
 +
 +        try {
 +            final JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
 +
 +            String value = jdbcTemplate.queryForObject(
 +                    "SELECT USERNAME FROM testsync WHERE ID=?", String.class, userTO.getKey());
 +            assertEquals("virtualvalue", value);
 +        } catch (EmptyResultDataAccessException e) {
 +            fail();
 +        }
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE313DB() throws Exception {
 +        // 1. create user in DB
 +        UserTO user = UserITCase.getUniqueSampleTO("syncope313-db@syncope.apache.org");
 +        user.setPassword("security123");
 +        user.getResources().add(RESOURCE_NAME_TESTDB);
 +        user = createUser(user);
 +        assertNotNull(user);
 +        assertFalse(user.getResources().isEmpty());
 +
 +        // 2. Check that the DB resource has the correct password
 +        final JdbcTemplate jdbcTemplate = new JdbcTemplate(testDataSource);
 +        String value = jdbcTemplate.queryForObject(
 +                "SELECT PASSWORD FROM test WHERE ID=?", String.class, user.getUsername());
 +        assertEquals(Encryptor.getInstance().encode("security123", CipherAlgorithm.SHA1), value.toUpperCase());
 +
 +        // 3. Update the password in the DB
 +        String newPassword = Encryptor.getInstance().encode("new-security", CipherAlgorithm.SHA1);
 +        jdbcTemplate.execute(
 +                "UPDATE test set PASSWORD='" + newPassword + "' where ID='" + user.getUsername() + "'");
 +
 +        // 4. Sync the user from the resource
 +        SyncTaskTO syncTask = new SyncTaskTO();
 +        syncTask.setDestinationRealm(SyncopeConstants.ROOT_REALM);
 +        syncTask.setName("DB Sync Task");
 +        syncTask.setPerformCreate(true);
 +        syncTask.setPerformUpdate(true);
 +        syncTask.setFullReconciliation(true);
 +        syncTask.setResource(RESOURCE_NAME_TESTDB);
 +        syncTask.getActionsClassNames().add(DBPasswordSyncActions.class.getName());
 +        Response taskResponse = taskService.create(syncTask);
 +
 +        SyncTaskTO actual = getObject(taskResponse.getLocation(), TaskService.class, SyncTaskTO.class);
 +        assertNotNull(actual);
 +
 +        syncTask = taskService.read(actual.getKey());
 +        assertNotNull(syncTask);
 +        assertEquals(actual.getKey(), syncTask.getKey());
 +        assertEquals(actual.getJobDelegateClassName(), syncTask.getJobDelegateClassName());
 +
 +        TaskExecTO execution = execProvisioningTask(taskService, syncTask.getKey(), 50, false);
 +        final String status = execution.getStatus();
 +        assertNotNull(status);
 +        assertTrue(PropagationTaskExecStatus.valueOf(status).isSuccessful());
 +
 +        // 5. Test the sync'd user
 +        UserTO updatedUser = userService.read(user.getKey());
 +        assertEquals(newPassword, updatedUser.getPassword());
 +
 +        // 6. Delete SyncTask + user
 +        taskService.delete(syncTask.getKey());
 +        deleteUser(user.getKey());
 +    }
 +
 +    @Test
 +    public void issueSYNCOPE313LDAP() throws Exception {
 +        // First of all, clear any potential conflict with existing user / group
 +        ldapCleanup();
 +
 +        // 1. create user in LDAP
 +        UserTO user = UserITCase.getUniqueSampleTO("syncope313-ldap@syncope.apache.org");
 +        user.setPassword("security123");
 +        user.getResources().add(RESOURCE_NAME_LDAP);
 +        user = createUser(user);
 +        assertNotNull(user);
 +        assertFalse(user.getResources().isEmpty());
 +
 +        // 2. request to change password only on Syncope and not on LDAP
 +        UserMod userMod = new UserMod();
 +        userMod.setKey(user.getKey());
 +        userMod.setPassword("new-security123");
 +        StatusMod pwdPropRequest = new StatusMod();
 +        pwdPropRequest.setOnSyncope(true);
-         pwdPropRequest.getResourceNames().clear();
++        pwdPropRequest.getResources().clear();
 +        userMod.setPwdPropRequest(pwdPropRequest);
 +        updateUser(userMod);
 +
 +        // 3. Check that the Syncope user now has the changed password
 +        UserTO updatedUser = userService.read(user.getKey());
 +        String encodedNewPassword = Encryptor.getInstance().encode("new-security123", CipherAlgorithm.SHA1);
 +        assertEquals(encodedNewPassword, updatedUser.getPassword());
 +
 +        // 4. Check that the LDAP resource has the old password
 +        ConnObjectTO connObject =
 +                resourceService.readConnObject(RESOURCE_NAME_LDAP, AnyTypeKind.USER.name(), user.getKey());
 +        assertNotNull(getLdapRemoteObject(
 +                connObject.getPlainAttrMap().get(Name.NAME).getValues().get(0),
 +                "security123",
 +                connObject.getPlainAttrMap().get(Name.NAME).getValues().get(0)));
 +
 +        // 5. Update the LDAP Connector to retrieve passwords
 +        ResourceTO ldapResource = resourceService.read(RESOURCE_NAME_LDAP);
 +        ConnInstanceTO resourceConnector = connectorService.read(
 +                ldapResource.getConnector(), Locale.ENGLISH.getLanguage());
 +        ConnConfProperty property = resourceConnector.getConfigurationMap().get("retrievePasswordsWithSearch");
 +        property.getValues().clear();
 +        property.getValues().add(Boolean.TRUE);
 +        connectorService.update(resourceConnector);
 +
 +        // 6. Sync the user from the resource
 +        SyncTaskTO syncTask = new SyncTaskTO();
 +        syncTask.setDestinationRealm(SyncopeConstants.ROOT_REALM);
 +        syncTask.setName("LDAP Sync Task");
 +        syncTask.setPerformCreate(true);
 +        syncTask.setPerformUpdate(true);
 +        syncTask.setFullReconciliation(true);
 +        syncTask.setResource(RESOURCE_NAME_LDAP);
 +        syncTask.getActionsClassNames().add(LDAPPasswordSyncActions.class.getName());
 +        Response taskResponse = taskService.create(syncTask);
 +
 +        SyncTaskTO actual = getObject(taskResponse.getLocation(), TaskService.class, SyncTaskTO.class);
 +        assertNotNull(actual);
 +
 +        syncTask = taskService.read(actual.getKey());
 +        assertNotNull(syncTask);
 +        assertEquals(actual.getKey(), syncTask.getKey());
 +        assertEquals(actual.getJobDelegateClassName(), syncTask.getJobDelegateClassName());
 +
 +        TaskExecTO execution = execProvisioningTask(taskService, syncTask.getKey(), 50, false);
 +        String status = execution.getStatus();
 +        assertNotNull(status);
 +        assertTrue(PropagationTaskExecStatus.valueOf(status).isSuccessful());
 +
 +        // 7. Test the sync'd user
 +        String syncedPassword = Encryptor.getInstance().encode("security123", CipherAlgorithm.SHA1);
 +        updatedUser = userService.read(user.getKey());
 +        assertEquals(syncedPassword, updatedUser.getPassword());
 +
 +        // 8. Delete SyncTask + user + reset the connector
 +        taskService.delete(syncTask.getKey());
 +        property.getValues().clear();
 +        property.getValues().add(Boolean.FALSE);
 +        connectorService.update(resourceConnector);
 +        deleteUser(updatedUser.getKey());
 +    }
 +}


[07/33] syncope git commit: Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/syncope

Posted by md...@apache.org.
Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/syncope


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

Branch: refs/heads/SYNCOPE-156
Commit: 8e219725d643ac5475257b85fc821de9521c64f0
Parents: 8502698 0e5694a
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Wed Aug 19 16:17:59 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Wed Aug 19 16:17:59 2015 +0200

----------------------------------------------------------------------
 .../syncope/client/console/pages/BasePage.java  |   5 +-
 .../console/panels/NotificationPanel.java       |   2 +-
 .../client/console/panels/ResourceModal.java    |   1 -
 .../client/console/topology/Topology.java       |   1 +
 .../console/topology/TopologyNodePanel.java     |  10 +-
 .../markup/html/form/ActionLinksPanel.java      | 192 ++++++++++++++++++-
 .../META-INF/resources/css/syncopeConsole.css   |   4 +
 .../syncope/client/console/pages/BasePage.html  |   2 +-
 8 files changed, 204 insertions(+), 13 deletions(-)
----------------------------------------------------------------------



[20/33] syncope git commit: Cleanly handling Quartz scheduler shutdown

Posted by md...@apache.org.
Cleanly handling Quartz scheduler shutdown


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

Branch: refs/heads/SYNCOPE-156
Commit: 56fe1eeca69eb222544da59ee07fafdb15a2016b
Parents: ec6bcf2
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Fri Aug 21 16:04:02 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Fri Aug 21 16:04:02 2015 +0200

----------------------------------------------------------------------
 .../java/job/SchedulerShutdown.java             | 49 ++++++++++++++++++++
 .../src/main/resources/provisioningContext.xml  |  1 +
 2 files changed, 50 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/56fe1eec/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SchedulerShutdown.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SchedulerShutdown.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SchedulerShutdown.java
new file mode 100644
index 0000000..8129928
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/SchedulerShutdown.java
@@ -0,0 +1,49 @@
+/*
+ * 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.provisioning.java.job;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.scheduling.quartz.SchedulerFactoryBean;
+
+/**
+ * Clean shutdown for Quartz scheduler.
+ */
+public class SchedulerShutdown implements ApplicationContextAware, DisposableBean {
+
+    private static final Logger LOG = LoggerFactory.getLogger(SchedulerShutdown.class);
+
+    private ApplicationContext ctx;
+
+    @Override
+    public void setApplicationContext(final ApplicationContext ctx) throws BeansException {
+        this.ctx = ctx;
+    }
+
+    @Override
+    public void destroy() throws Exception {
+        SchedulerFactoryBean scheduler = ctx.getBean(SchedulerFactoryBean.class);
+        scheduler.getScheduler().shutdown();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/56fe1eec/core/provisioning-java/src/main/resources/provisioningContext.xml
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/resources/provisioningContext.xml b/core/provisioning-java/src/main/resources/provisioningContext.xml
index 2f0f617..4d635d2 100644
--- a/core/provisioning-java/src/main/resources/provisioningContext.xml
+++ b/core/provisioning-java/src/main/resources/provisioningContext.xml
@@ -81,6 +81,7 @@ under the License.
       </props>
     </property>
   </bean>
+  <bean class="org.apache.syncope.core.provisioning.java.job.SchedulerShutdown"/>
   
   <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
     <property name="defaultEncoding" value="${smtpEncoding}"/>


[30/33] syncope git commit: [SYNCOPE-687] Minimal README.md added

Posted by md...@apache.org.
[SYNCOPE-687] Minimal README.md added


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

Branch: refs/heads/SYNCOPE-156
Commit: 4412ac7b858793145c640e1e3a723128f3171f04
Parents: 054ea9c
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Wed Aug 26 17:34:47 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Wed Aug 26 17:34:47 2015 +0200

----------------------------------------------------------------------
 README.md | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/4412ac7b/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5445635
--- /dev/null
+++ b/README.md
@@ -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.
+-->
+**Apache Syncope** is an Open Source system for managing digital identities in enterprise environments, 
+implemented in JEE technology and released under Apache 2.0 license.
+
+More information at http://syncope.apache.org
+
+<a href="https://travis-ci.org/apache/syncope"><img src="https://api.travis-ci.org/apache/syncope.png"/></a>
\ No newline at end of file


[19/33] syncope git commit: Fixing archetype AND standalone after SYNCOPE-620

Posted by md...@apache.org.
Fixing archetype AND standalone after SYNCOPE-620


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

Branch: refs/heads/SYNCOPE-156
Commit: ec6bcf2f8979b76c7708f9590c786422bad56255
Parents: 213fbc6
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Fri Aug 21 15:19:12 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Fri Aug 21 15:19:12 2015 +0200

----------------------------------------------------------------------
 archetype/pom.xml                               |  4 +-
 .../archetype-resources/console/pom.xml         |  3 +-
 .../test/resources/domains/MasterContent.xml    |  2 +-
 .../src/test/resources/domains/Two.properties   |  2 +-
 .../resources/scriptedsql/CreateScript.groovy   | 52 -----------
 .../resources/scriptedsql/DeleteScript.groovy   | 43 ---------
 .../resources/scriptedsql/SchemaScript.groovy   | 50 ----------
 .../resources/scriptedsql/SearchScript.groovy   | 94 -------------------
 .../resources/scriptedsql/SyncScript.groovy     | 98 --------------------
 .../resources/scriptedsql/TestScript.groovy     | 31 -------
 .../resources/scriptedsql/UpdateScript.groovy   | 67 -------------
 .../resources/scriptedsql/CreateScript.groovy   | 52 +++++++++++
 .../resources/scriptedsql/DeleteScript.groovy   | 43 +++++++++
 .../resources/scriptedsql/SchemaScript.groovy   | 50 ++++++++++
 .../resources/scriptedsql/SearchScript.groovy   | 94 +++++++++++++++++++
 .../resources/scriptedsql/SyncScript.groovy     | 98 ++++++++++++++++++++
 .../resources/scriptedsql/TestScript.groovy     | 31 +++++++
 .../resources/scriptedsql/UpdateScript.groovy   | 67 +++++++++++++
 pom.xml                                         |  2 +-
 standalone/pom.xml                              | 16 +++-
 standalone/src/main/resources/setenv.bat        |  2 +-
 standalone/src/main/resources/setenv.sh         |  2 +-
 22 files changed, 457 insertions(+), 446 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/ec6bcf2f/archetype/pom.xml
----------------------------------------------------------------------
diff --git a/archetype/pom.xml b/archetype/pom.xml
index f22303b..a921a3c 100644
--- a/archetype/pom.xml
+++ b/archetype/pom.xml
@@ -175,8 +175,8 @@ under the License.
         </includes>
       </resource>
       <resource>
-        <directory>../fit/core-reference/src/main/resources/scriptedsql</directory>
-        <targetPath>${project.build.outputDirectory}/archetype-resources/core/src/main/resources/scriptedsql</targetPath>
+        <directory>../fit/core-reference/src/test/resources/scriptedsql</directory>
+        <targetPath>${project.build.outputDirectory}/archetype-resources/core/src/test/resources/scriptedsql</targetPath>
       </resource>
       <resource>
         <directory>../fit/core-reference/src/main/resources</directory>

http://git-wip-us.apache.org/repos/asf/syncope/blob/ec6bcf2f/archetype/src/main/resources/archetype-resources/console/pom.xml
----------------------------------------------------------------------
diff --git a/archetype/src/main/resources/archetype-resources/console/pom.xml b/archetype/src/main/resources/archetype-resources/console/pom.xml
index 2a4ad8f..53bd556 100644
--- a/archetype/src/main/resources/archetype-resources/console/pom.xml
+++ b/archetype/src/main/resources/archetype-resources/console/pom.xml
@@ -231,7 +231,8 @@ ORYX.Editor.createByUrl = function(modelUrl){"/>
                 <phase>package</phase>
                 <configuration>
                   <target>
-                    <copy todir="../core/target/syncope/WEB-INF/classes">
+                    <delete dir="../core/target/syncope/WEB-INF/classes/domains"/>
+                    <copy todir="../core/target/syncope/WEB-INF/classes/domains">
                       <fileset dir="../core/target/test-classes/domains"/>
                     </copy>
                     <copy file="../core/target/test-classes/connid.properties" 

http://git-wip-us.apache.org/repos/asf/syncope/blob/ec6bcf2f/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index 50e7b06..3401bb7 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -592,7 +592,7 @@ under the License.
                 location="connid://${testconnectorserver.key}@localhost:${testconnectorserver.port}"
                 connectorName="net.tirasa.connid.bundles.db.scriptedsql.ScriptedSQLConnector"
                 displayName="Scripted SQL" version="${connid.database.version}"
-                jsonConf='[{&quot;schema&quot;:{&quot;name&quot;:&quot;updateScriptFileName&quot;,&quot;displayName&quot;:&quot;updateScriptFileName&quot;,&quot;helpMessage&quot;:&quot;updateScriptFileName&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[]},&quot;overridable&quot;:false,&quot;values&quot;:[&quot;${basedir}/src/main/resources/scriptedsql/UpdateScript.groovy&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;testScript&quot;,&quot;displayName&quot;:&quot;testScript&quot;,&quot;helpMessage&quot;:&quot;testScript&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;&quot;]},&quot;overridable&quot;:false,&quot;values&quot;:[]},{&quot;schema&quot;:{&quot;name&quot;:&quot;host&quot;,&quot;displayName&quot;:&quot;Host&quot;,&quot;helpMessage&quot;:&quot;&lt;b
 &gt;Host&lt;/b&gt;&lt;br/&gt;Enter the name of the host where the database is running.&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:2,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;localhost&quot;]},&quot;overridable&quot;:false},{&quot;schema&quot;:{&quot;name&quot;:&quot;port&quot;,&quot;displayName&quot;:&quot;Port&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;TCP Port&lt;/b&gt;&lt;br/&gt;Enter the port number the database server is listening on.&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:3,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;3306&quot;]},&quot;overridable&quot;:false},{&quot;schema&quot;:{&quot;name&quot;:&quot;database&quot;,&quot;displayName&quot;:&quot;Database&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;Database&lt;/b&gt;&lt;br/&gt;Enter the name of the database on the database server that contains the table.&quot;,&quot;type&
 quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:6,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;&quot;]},&quot;overridable&quot;:false},{&quot;schema&quot;:{&quot;name&quot;:&quot;createScript&quot;,&quot;displayName&quot;:&quot;createScript&quot;,&quot;helpMessage&quot;:&quot;createScript&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;&quot;]},&quot;overridable&quot;:false,&quot;values&quot;:[]},{&quot;schema&quot;:{&quot;name&quot;:&quot;jdbcUrlTemplate&quot;,&quot;displayName&quot;:&quot;JDBC Connection URL&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;JDBC Connection URL&lt;/b&gt;&lt;br/&gt;Specify the JDBC Driver Connection URL.&lt;br/&gt; Oracle template is jdbc:oracle:thin:@[host]:[port(1521)]:[DB].&lt;br/&gt;  MySQL template is jdbc:mysql://[host]:[port(3306)]/[db], for more info, read the JDBC driver documentati
 on.&lt;br/&gt;Could be empty if datasource is provided.&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:11,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;jdbc:mysql://%h:%p/%d&quot;]},&quot;overridable&quot;:false,&quot;values&quot;:[&quot;${testdb.url}&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;jndiProperties&quot;,&quot;displayName&quot;:&quot;Initial JNDI Properties&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;Initial JNDI Properties&lt;/b&gt;&lt;br/&gt;Could be empty or enter the JDBC JNDI Initial context factory, context provider in a format: key = value.&quot;,&quot;type&quot;:&quot;[Ljava.lang.String;&quot;,&quot;required&quot;:false,&quot;order&quot;:21,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[]},&quot;overridable&quot;:false,&quot;values&quot;:[]},{&quot;schema&quot;:{&quot;name&quot;:&quot;enableEmptyString&quot;,&quot;displayName&quot;:&quot;Enable writing empty string&quot;,&quot;
 helpMessage&quot;:&quot;&lt;b&gt;Enable writing empty string&lt;/b&gt;&lt;br/&gt;Select to enable support for writing an empty strings, instead of a NULL value, in character based columns defined as not-null in the table schema. This option does not influence the way strings are written for Oracle based tables. By default empty strings are written as a NULL value.&quot;,&quot;type&quot;:&quot;boolean&quot;,&quot;required&quot;:false,&quot;order&quot;:12,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[false]},&quot;overridable&quot;:false,&quot;values&quot;:[&quot;false&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;allNative&quot;,&quot;displayName&quot;:&quot;All native&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;All native&lt;/b&gt;&lt;br/&gt;Select to retrieve all data type of the columns in a native format from the database table.&quot;,&quot;type&quot;:&quot;boolean&quot;,&quot;required&quot;:false,&quot;order&quot;:16,&quot;confidential&quot;:false,&quot;defaul
 tValues&quot;:[false]},&quot;overridable&quot;:false,&quot;values&quot;:[false]},{&quot;schema&quot;:{&quot;name&quot;:&quot;password&quot;,&quot;displayName&quot;:&quot;User Password&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;User Password&lt;/b&gt;&lt;br/&gt;Enter a user account that has permission to access accounts table.&quot;,&quot;type&quot;:&quot;org.identityconnectors.common.security.GuardedString&quot;,&quot;required&quot;:false,&quot;order&quot;:5,&quot;confidential&quot;:true,&quot;defaultValues&quot;:[]},&quot;overridable&quot;:false,&quot;values&quot;:[&quot;${testdb.password}&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;validConnectionQuery&quot;,&quot;displayName&quot;:&quot;Validate Connection Query&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;Validate Connection Query&lt;/b&gt;&lt;br/&gt;There can be specified the check connection alive query. If empty, default implementation will test it using the switch on/off the autocommit. Some select 1 from dummy tab
 le could be more efficient.&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:17,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[]},&quot;overridable&quot;:false,&quot;values&quot;:[]},{&quot;schema&quot;:{&quot;name&quot;:&quot;reloadScriptOnExecution&quot;,&quot;displayName&quot;:&quot;reloadScriptOnExecution&quot;,&quot;helpMessage&quot;:&quot;reloadScriptOnExecution&quot;,&quot;type&quot;:&quot;boolean&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[false]},&quot;overridable&quot;:false,&quot;values&quot;:[&quot;true&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;schemaScriptFileName&quot;,&quot;displayName&quot;:&quot;schemaScriptFileName&quot;,&quot;helpMessage&quot;:&quot;schemaScriptFileName&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[]},&qu
 ot;overridable&quot;:true,&quot;values&quot;:[&quot;${basedir}/src/main/resources/scriptedsql/SchemaScript.groovy&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;jdbcDriver&quot;,&quot;displayName&quot;:&quot;JDBC Driver&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;JDBC Driver&lt;/b&gt;&lt;br/&gt;Specify the JDBC Driver class name. Oracle is oracle.jdbc.driver.OracleDriver. MySQL is org.gjt.mm.mysql.Driver.&lt;br/&gt;Could be empty if datasource is provided.&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:10,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;com.mysql.jdbc.Driver&quot;]},&quot;overridable&quot;:false,&quot;values&quot;:[&quot;${testdb.driver}&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;testScriptFileName&quot;,&quot;displayName&quot;:&quot;testScriptFileName&quot;,&quot;helpMessage&quot;:&quot;testScriptFileName&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&qu
 ot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[]},&quot;overridable&quot;:true,&quot;values&quot;:[&quot;${basedir}/src/main/resources/scriptedsql/TestScript.groovy&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;quoting&quot;,&quot;displayName&quot;:&quot;Name Quoting&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;Name Quoting&lt;/b&gt;&lt;br/&gt;Select whether database column names for this resource should be quoted, and the quoting characters. By default, database column names are not quoted (None). For other selections (Single, Double, Back, or Brackets), column names will appear between single quotes, double quotes, back quotes, or brackets in the SQL generated to access the database.&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:-1,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;&quot;]},&quot;overridable&quot;:false,&quot;values&quot;:[]},{&quot;schema&quot;:{&quot;name&quot;:&quo
 t;createScriptFileName&quot;,&quot;displayName&quot;:&quot;createScriptFileName&quot;,&quot;helpMessage&quot;:&quot;createScriptFileName&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[]},&quot;overridable&quot;:false,&quot;values&quot;:[&quot;${basedir}/src/main/resources/scriptedsql/CreateScript.groovy&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;clearTextPasswordToScript&quot;,&quot;displayName&quot;:&quot;clearTextPasswordToScript&quot;,&quot;helpMessage&quot;:&quot;clearTextPasswordToScript&quot;,&quot;type&quot;:&quot;boolean&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[true]},&quot;overridable&quot;:false,&quot;values&quot;:[&quot;false&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;nativeTimestamps&quot;,&quot;displayName&quot;:&quot;Native Timestamps&quot;,&quot;helpMessage&quot;:&quot;&lt;
 b&gt;Native Timestamps&lt;/b&gt;&lt;br/&gt;Select to retrieve Timestamp data type of the columns in java.sql.Timestamp format from the database table.&quot;,&quot;type&quot;:&quot;boolean&quot;,&quot;required&quot;:false,&quot;order&quot;:15,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[false]},&quot;overridable&quot;:false,&quot;values&quot;:[false]},{&quot;schema&quot;:{&quot;name&quot;:&quot;syncScript&quot;,&quot;displayName&quot;:&quot;syncScript&quot;,&quot;helpMessage&quot;:&quot;syncScript&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;&quot;]},&quot;overridable&quot;:false,&quot;values&quot;:[]},{&quot;schema&quot;:{&quot;name&quot;:&quot;autoCommit&quot;,&quot;displayName&quot;:&quot;autoCommit&quot;,&quot;helpMessage&quot;:&quot;autoCommit&quot;,&quot;type&quot;:&quot;boolean&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential
 &quot;:false,&quot;defaultValues&quot;:[true]},&quot;overridable&quot;:false,&quot;values&quot;:[true]},{&quot;schema&quot;:{&quot;name&quot;:&quot;scriptingLanguage&quot;,&quot;displayName&quot;:&quot;scriptingLanguage&quot;,&quot;helpMessage&quot;:&quot;scriptingLanguage&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;GROOVY&quot;]},&quot;overridable&quot;:false,&quot;values&quot;:[&quot;GROOVY&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;datasource&quot;,&quot;displayName&quot;:&quot;Datasource Path&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;JDBC Data Source Name/Path&lt;/b&gt;&lt;br/&gt;Enter the JDBC Data Source Name/Path to connect to the Oracle server. If specified, connector will only try to connect using Datasource and ignore other resource parameters specified.&lt;br/&gt;the example value is: &lt;CODE&gt;jdbc/SampleDataSourceName&lt;/CODE&gt;&quot;,&q
 uot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:20,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;&quot;]},&quot;overridable&quot;:false,&quot;values&quot;:[]},{&quot;schema&quot;:{&quot;name&quot;:&quot;deleteScript&quot;,&quot;displayName&quot;:&quot;deleteScript&quot;,&quot;helpMessage&quot;:&quot;deleteScript&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;&quot;]},&quot;overridable&quot;:false,&quot;values&quot;:[]},{&quot;schema&quot;:{&quot;name&quot;:&quot;rethrowAllSQLExceptions&quot;,&quot;displayName&quot;:&quot;Rethrow all SQLExceptions&quot;,&quot;helpMessage&quot;:&quot;If this is not checked, SQL statements which throw SQLExceptions with a 0 ErrorCode will be have the exception caught and suppressed. Check it to have exceptions with 0 ErrorCodes rethrown.&quot;,&quot;type&quot;:&quot;boolean&quot;,
 &quot;required&quot;:false,&quot;order&quot;:14,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[true]},&quot;overridable&quot;:false,&quot;values&quot;:[true]},{&quot;schema&quot;:{&quot;name&quot;:&quot;syncScriptFileName&quot;,&quot;displayName&quot;:&quot;syncScriptFileName&quot;,&quot;helpMessage&quot;:&quot;syncScriptFileName&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[]},&quot;overridable&quot;:true,&quot;values&quot;:[&quot;${basedir}/src/main/resources/scriptedsql/SyncScript.groovy&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;updateScript&quot;,&quot;displayName&quot;:&quot;updateScript&quot;,&quot;helpMessage&quot;:&quot;updateScript&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;&quot;]},&quot;overridable&quot;:false,&quot;values&q
 uot;:[]},{&quot;schema&quot;:{&quot;name&quot;:&quot;user&quot;,&quot;displayName&quot;:&quot;User&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;User&lt;/b&gt;&lt;br/&gt;Enter the name of the mandatory Database user with permission to account table.&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:4,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;&quot;]},&quot;overridable&quot;:false,&quot;values&quot;:[&quot;${testdb.username}&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;deleteScriptFileName&quot;,&quot;displayName&quot;:&quot;deleteScriptFileName&quot;,&quot;helpMessage&quot;:&quot;deleteScriptFileName&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[]},&quot;overridable&quot;:false,&quot;values&quot;:[&quot;${basedir}/src/main/resources/scriptedsql/DeleteScript.groovy&quot;]},{&quot;schema&quot;:{&quot;n
 ame&quot;:&quot;searchScriptFileName&quot;,&quot;displayName&quot;:&quot;searchScriptFileName&quot;,&quot;helpMessage&quot;:&quot;searchScriptFileName&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[]},&quot;overridable&quot;:true,&quot;values&quot;:[&quot;${basedir}/src/main/resources/scriptedsql/SearchScript.groovy&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;searchScript&quot;,&quot;displayName&quot;:&quot;searchScript&quot;,&quot;helpMessage&quot;:&quot;searchScript&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;&quot;]},&quot;overridable&quot;:false,&quot;values&quot;:[]}]'/>
+                jsonConf='[{&quot;schema&quot;:{&quot;name&quot;:&quot;updateScriptFileName&quot;,&quot;displayName&quot;:&quot;updateScriptFileName&quot;,&quot;helpMessage&quot;:&quot;updateScriptFileName&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[]},&quot;overridable&quot;:false,&quot;values&quot;:[&quot;${conf.directory}/scriptedsql/UpdateScript.groovy&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;testScript&quot;,&quot;displayName&quot;:&quot;testScript&quot;,&quot;helpMessage&quot;:&quot;testScript&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;&quot;]},&quot;overridable&quot;:false,&quot;values&quot;:[]},{&quot;schema&quot;:{&quot;name&quot;:&quot;host&quot;,&quot;displayName&quot;:&quot;Host&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;Host&lt;
 /b&gt;&lt;br/&gt;Enter the name of the host where the database is running.&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:2,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;localhost&quot;]},&quot;overridable&quot;:false},{&quot;schema&quot;:{&quot;name&quot;:&quot;port&quot;,&quot;displayName&quot;:&quot;Port&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;TCP Port&lt;/b&gt;&lt;br/&gt;Enter the port number the database server is listening on.&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:3,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;3306&quot;]},&quot;overridable&quot;:false},{&quot;schema&quot;:{&quot;name&quot;:&quot;database&quot;,&quot;displayName&quot;:&quot;Database&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;Database&lt;/b&gt;&lt;br/&gt;Enter the name of the database on the database server that contains the table.&quot;,&quot;type&quot;:&quot;
 java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:6,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;&quot;]},&quot;overridable&quot;:false},{&quot;schema&quot;:{&quot;name&quot;:&quot;createScript&quot;,&quot;displayName&quot;:&quot;createScript&quot;,&quot;helpMessage&quot;:&quot;createScript&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;&quot;]},&quot;overridable&quot;:false,&quot;values&quot;:[]},{&quot;schema&quot;:{&quot;name&quot;:&quot;jdbcUrlTemplate&quot;,&quot;displayName&quot;:&quot;JDBC Connection URL&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;JDBC Connection URL&lt;/b&gt;&lt;br/&gt;Specify the JDBC Driver Connection URL.&lt;br/&gt; Oracle template is jdbc:oracle:thin:@[host]:[port(1521)]:[DB].&lt;br/&gt;  MySQL template is jdbc:mysql://[host]:[port(3306)]/[db], for more info, read the JDBC driver documentation.&lt;br/&g
 t;Could be empty if datasource is provided.&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:11,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;jdbc:mysql://%h:%p/%d&quot;]},&quot;overridable&quot;:false,&quot;values&quot;:[&quot;${testdb.url}&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;jndiProperties&quot;,&quot;displayName&quot;:&quot;Initial JNDI Properties&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;Initial JNDI Properties&lt;/b&gt;&lt;br/&gt;Could be empty or enter the JDBC JNDI Initial context factory, context provider in a format: key = value.&quot;,&quot;type&quot;:&quot;[Ljava.lang.String;&quot;,&quot;required&quot;:false,&quot;order&quot;:21,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[]},&quot;overridable&quot;:false,&quot;values&quot;:[]},{&quot;schema&quot;:{&quot;name&quot;:&quot;enableEmptyString&quot;,&quot;displayName&quot;:&quot;Enable writing empty string&quot;,&quot;helpMessage&
 quot;:&quot;&lt;b&gt;Enable writing empty string&lt;/b&gt;&lt;br/&gt;Select to enable support for writing an empty strings, instead of a NULL value, in character based columns defined as not-null in the table schema. This option does not influence the way strings are written for Oracle based tables. By default empty strings are written as a NULL value.&quot;,&quot;type&quot;:&quot;boolean&quot;,&quot;required&quot;:false,&quot;order&quot;:12,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[false]},&quot;overridable&quot;:false,&quot;values&quot;:[&quot;false&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;allNative&quot;,&quot;displayName&quot;:&quot;All native&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;All native&lt;/b&gt;&lt;br/&gt;Select to retrieve all data type of the columns in a native format from the database table.&quot;,&quot;type&quot;:&quot;boolean&quot;,&quot;required&quot;:false,&quot;order&quot;:16,&quot;confidential&quot;:false,&quot;defaultValues&quot
 ;:[false]},&quot;overridable&quot;:false,&quot;values&quot;:[false]},{&quot;schema&quot;:{&quot;name&quot;:&quot;password&quot;,&quot;displayName&quot;:&quot;User Password&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;User Password&lt;/b&gt;&lt;br/&gt;Enter a user account that has permission to access accounts table.&quot;,&quot;type&quot;:&quot;org.identityconnectors.common.security.GuardedString&quot;,&quot;required&quot;:false,&quot;order&quot;:5,&quot;confidential&quot;:true,&quot;defaultValues&quot;:[]},&quot;overridable&quot;:false,&quot;values&quot;:[&quot;${testdb.password}&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;validConnectionQuery&quot;,&quot;displayName&quot;:&quot;Validate Connection Query&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;Validate Connection Query&lt;/b&gt;&lt;br/&gt;There can be specified the check connection alive query. If empty, default implementation will test it using the switch on/off the autocommit. Some select 1 from dummy table could be 
 more efficient.&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:17,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[]},&quot;overridable&quot;:false,&quot;values&quot;:[]},{&quot;schema&quot;:{&quot;name&quot;:&quot;reloadScriptOnExecution&quot;,&quot;displayName&quot;:&quot;reloadScriptOnExecution&quot;,&quot;helpMessage&quot;:&quot;reloadScriptOnExecution&quot;,&quot;type&quot;:&quot;boolean&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[false]},&quot;overridable&quot;:false,&quot;values&quot;:[&quot;true&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;schemaScriptFileName&quot;,&quot;displayName&quot;:&quot;schemaScriptFileName&quot;,&quot;helpMessage&quot;:&quot;schemaScriptFileName&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[]},&quot;overridab
 le&quot;:true,&quot;values&quot;:[&quot;${conf.directory}/scriptedsql/SchemaScript.groovy&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;jdbcDriver&quot;,&quot;displayName&quot;:&quot;JDBC Driver&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;JDBC Driver&lt;/b&gt;&lt;br/&gt;Specify the JDBC Driver class name. Oracle is oracle.jdbc.driver.OracleDriver. MySQL is org.gjt.mm.mysql.Driver.&lt;br/&gt;Could be empty if datasource is provided.&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:10,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;com.mysql.jdbc.Driver&quot;]},&quot;overridable&quot;:false,&quot;values&quot;:[&quot;${testdb.driver}&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;testScriptFileName&quot;,&quot;displayName&quot;:&quot;testScriptFileName&quot;,&quot;helpMessage&quot;:&quot;testScriptFileName&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;c
 onfidential&quot;:false,&quot;defaultValues&quot;:[]},&quot;overridable&quot;:true,&quot;values&quot;:[&quot;${conf.directory}/scriptedsql/TestScript.groovy&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;quoting&quot;,&quot;displayName&quot;:&quot;Name Quoting&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;Name Quoting&lt;/b&gt;&lt;br/&gt;Select whether database column names for this resource should be quoted, and the quoting characters. By default, database column names are not quoted (None). For other selections (Single, Double, Back, or Brackets), column names will appear between single quotes, double quotes, back quotes, or brackets in the SQL generated to access the database.&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:-1,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;&quot;]},&quot;overridable&quot;:false,&quot;values&quot;:[]},{&quot;schema&quot;:{&quot;name&quot;:&quot;createScriptFileName&quot;,&quot;d
 isplayName&quot;:&quot;createScriptFileName&quot;,&quot;helpMessage&quot;:&quot;createScriptFileName&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[]},&quot;overridable&quot;:false,&quot;values&quot;:[&quot;${conf.directory}/scriptedsql/CreateScript.groovy&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;clearTextPasswordToScript&quot;,&quot;displayName&quot;:&quot;clearTextPasswordToScript&quot;,&quot;helpMessage&quot;:&quot;clearTextPasswordToScript&quot;,&quot;type&quot;:&quot;boolean&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[true]},&quot;overridable&quot;:false,&quot;values&quot;:[&quot;false&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;nativeTimestamps&quot;,&quot;displayName&quot;:&quot;Native Timestamps&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;Native Timestamps&lt;/b&gt;&lt;br/&gt;Selec
 t to retrieve Timestamp data type of the columns in java.sql.Timestamp format from the database table.&quot;,&quot;type&quot;:&quot;boolean&quot;,&quot;required&quot;:false,&quot;order&quot;:15,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[false]},&quot;overridable&quot;:false,&quot;values&quot;:[false]},{&quot;schema&quot;:{&quot;name&quot;:&quot;syncScript&quot;,&quot;displayName&quot;:&quot;syncScript&quot;,&quot;helpMessage&quot;:&quot;syncScript&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;&quot;]},&quot;overridable&quot;:false,&quot;values&quot;:[]},{&quot;schema&quot;:{&quot;name&quot;:&quot;autoCommit&quot;,&quot;displayName&quot;:&quot;autoCommit&quot;,&quot;helpMessage&quot;:&quot;autoCommit&quot;,&quot;type&quot;:&quot;boolean&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[true]},&
 quot;overridable&quot;:false,&quot;values&quot;:[true]},{&quot;schema&quot;:{&quot;name&quot;:&quot;scriptingLanguage&quot;,&quot;displayName&quot;:&quot;scriptingLanguage&quot;,&quot;helpMessage&quot;:&quot;scriptingLanguage&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;GROOVY&quot;]},&quot;overridable&quot;:false,&quot;values&quot;:[&quot;GROOVY&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;datasource&quot;,&quot;displayName&quot;:&quot;Datasource Path&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;JDBC Data Source Name/Path&lt;/b&gt;&lt;br/&gt;Enter the JDBC Data Source Name/Path to connect to the Oracle server. If specified, connector will only try to connect using Datasource and ignore other resource parameters specified.&lt;br/&gt;the example value is: &lt;CODE&gt;jdbc/SampleDataSourceName&lt;/CODE&gt;&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quo
 t;required&quot;:false,&quot;order&quot;:20,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;&quot;]},&quot;overridable&quot;:false,&quot;values&quot;:[]},{&quot;schema&quot;:{&quot;name&quot;:&quot;deleteScript&quot;,&quot;displayName&quot;:&quot;deleteScript&quot;,&quot;helpMessage&quot;:&quot;deleteScript&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;&quot;]},&quot;overridable&quot;:false,&quot;values&quot;:[]},{&quot;schema&quot;:{&quot;name&quot;:&quot;rethrowAllSQLExceptions&quot;,&quot;displayName&quot;:&quot;Rethrow all SQLExceptions&quot;,&quot;helpMessage&quot;:&quot;If this is not checked, SQL statements which throw SQLExceptions with a 0 ErrorCode will be have the exception caught and suppressed. Check it to have exceptions with 0 ErrorCodes rethrown.&quot;,&quot;type&quot;:&quot;boolean&quot;,&quot;required&quot;:false,&quot;order&quot;:14,
 &quot;confidential&quot;:false,&quot;defaultValues&quot;:[true]},&quot;overridable&quot;:false,&quot;values&quot;:[true]},{&quot;schema&quot;:{&quot;name&quot;:&quot;syncScriptFileName&quot;,&quot;displayName&quot;:&quot;syncScriptFileName&quot;,&quot;helpMessage&quot;:&quot;syncScriptFileName&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[]},&quot;overridable&quot;:true,&quot;values&quot;:[&quot;${conf.directory}/scriptedsql/SyncScript.groovy&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;updateScript&quot;,&quot;displayName&quot;:&quot;updateScript&quot;,&quot;helpMessage&quot;:&quot;updateScript&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;&quot;]},&quot;overridable&quot;:false,&quot;values&quot;:[]},{&quot;schema&quot;:{&quot;name&quot;:&quot;user&qu
 ot;,&quot;displayName&quot;:&quot;User&quot;,&quot;helpMessage&quot;:&quot;&lt;b&gt;User&lt;/b&gt;&lt;br/&gt;Enter the name of the mandatory Database user with permission to account table.&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:4,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;&quot;]},&quot;overridable&quot;:false,&quot;values&quot;:[&quot;${testdb.username}&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;deleteScriptFileName&quot;,&quot;displayName&quot;:&quot;deleteScriptFileName&quot;,&quot;helpMessage&quot;:&quot;deleteScriptFileName&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[]},&quot;overridable&quot;:false,&quot;values&quot;:[&quot;${conf.directory}/scriptedsql/DeleteScript.groovy&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;searchScriptFileName&quot;,&quot;displayName&quot;:&quot
 ;searchScriptFileName&quot;,&quot;helpMessage&quot;:&quot;searchScriptFileName&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[]},&quot;overridable&quot;:true,&quot;values&quot;:[&quot;${conf.directory}/scriptedsql/SearchScript.groovy&quot;]},{&quot;schema&quot;:{&quot;name&quot;:&quot;searchScript&quot;,&quot;displayName&quot;:&quot;searchScript&quot;,&quot;helpMessage&quot;:&quot;searchScript&quot;,&quot;type&quot;:&quot;java.lang.String&quot;,&quot;required&quot;:false,&quot;order&quot;:0,&quot;confidential&quot;:false,&quot;defaultValues&quot;:[&quot;&quot;]},&quot;overridable&quot;:false,&quot;values&quot;:[]}]'/>
   <ConnInstance_capabilities ConnInstance_id="108" capability="ONE_PHASE_CREATE"/>
   <ConnInstance_capabilities ConnInstance_id="108" capability="ONE_PHASE_UPDATE"/>
   <ConnInstance_capabilities ConnInstance_id="108" capability="ONE_PHASE_DELETE"/>

http://git-wip-us.apache.org/repos/asf/syncope/blob/ec6bcf2f/core/persistence-jpa/src/test/resources/domains/Two.properties
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/resources/domains/Two.properties b/core/persistence-jpa/src/test/resources/domains/Two.properties
index b37a969..479b223 100644
--- a/core/persistence-jpa/src/test/resources/domains/Two.properties
+++ b/core/persistence-jpa/src/test/resources/domains/Two.properties
@@ -15,7 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 Two.driverClassName=org.h2.Driver
-Two.url=jdbc:h2:file:${project.build.directory}/test-classes/syncopetwo.db;DB_CLOSE_DELAY=-1
+Two.url=jdbc:h2:file:${conf.directory}/syncopetwo.db
 Two.schema=
 Two.username=sa
 Two.password=

http://git-wip-us.apache.org/repos/asf/syncope/blob/ec6bcf2f/fit/core-reference/src/main/resources/scriptedsql/CreateScript.groovy
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/scriptedsql/CreateScript.groovy b/fit/core-reference/src/main/resources/scriptedsql/CreateScript.groovy
deleted file mode 100644
index 52c10f9..0000000
--- a/fit/core-reference/src/main/resources/scriptedsql/CreateScript.groovy
+++ /dev/null
@@ -1,52 +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.
- */
-import groovy.sql.Sql;
-import groovy.sql.DataSet;
-
-// Parameters:
-// The connector sends us the following:
-// connection : SQL connection
-// action: String correponding to the action ("CREATE" here)
-// log: a handler to the Log facility
-// objectClass: a String describing the Object class (__ACCOUNT__ / __GROUP__ / other)
-// id: The entry identifier (OpenICF "Name" atribute. (most often matches the uid)
-// attributes: an Attribute Map, containg the <String> attribute name as a key
-// and the <List> attribute value(s) as value.
-// password: password string, clear text
-// options: a handler to the OperationOptions Map
-
-log.info("Entering " + action + " Script");
-
-def sql = new Sql(connection);
-
-switch ( objectClass ) {
-case "__PRINTER__":
-  sql.execute("INSERT INTO TESTPRINTER (id, location, lastmodification) values (?,?,?)",
-    [
-      id,
-      attributes.get("location").get(0),
-      new Date()
-    ])
-  break
-
-default:
-  id;
-}
-
-return id;

http://git-wip-us.apache.org/repos/asf/syncope/blob/ec6bcf2f/fit/core-reference/src/main/resources/scriptedsql/DeleteScript.groovy
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/scriptedsql/DeleteScript.groovy b/fit/core-reference/src/main/resources/scriptedsql/DeleteScript.groovy
deleted file mode 100644
index cdd7f5b..0000000
--- a/fit/core-reference/src/main/resources/scriptedsql/DeleteScript.groovy
+++ /dev/null
@@ -1,43 +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.
- */
-import groovy.sql.Sql;
-import groovy.sql.DataSet;
-
-// Parameters:
-// The connector sends the following:
-// connection: handler to the SQL connection
-// action: a string describing the action ("DELETE" here)
-// log: a handler to the Log facility
-// objectClass: a String describing the Object class (__ACCOUNT__ / __GROUP__ / other)
-// options: a handler to the OperationOptions Map
-// uid: String for the unique id that specifies the object to delete
-
-log.info("Entering " + action + " Script");
-def sql = new Sql(connection);
-
-assert uid != null
-
-switch ( objectClass ) {
-case "__PRINTER__":
-  sql.execute("DELETE FROM TESTPRINTER where id= ?",[uid])
-  break
-
-default:
-  uid;
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ec6bcf2f/fit/core-reference/src/main/resources/scriptedsql/SchemaScript.groovy
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/scriptedsql/SchemaScript.groovy b/fit/core-reference/src/main/resources/scriptedsql/SchemaScript.groovy
deleted file mode 100644
index 50f8a8a..0000000
--- a/fit/core-reference/src/main/resources/scriptedsql/SchemaScript.groovy
+++ /dev/null
@@ -1,50 +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.
- */
-import org.identityconnectors.framework.common.objects.AttributeInfo;
-import org.identityconnectors.framework.common.objects.AttributeInfo.Flags;
-import org.identityconnectors.framework.common.objects.AttributeInfoBuilder;
-import org.identityconnectors.framework.common.objects.ObjectClassInfo;
-import org.identityconnectors.framework.common.objects.ObjectClassInfoBuilder;
-
-// Parameters:
-// The connector sends the following:
-// action: a string describing the action ("SCHEMA" here)
-// log: a handler to the Log facility
-// builder: SchemaBuilder instance for the connector
-//
-// The connector will make the final call to builder.build()
-// so the scipt just need to declare the different object types.
-
-// This sample shows how to create 3 basic ObjectTypes: __ACCOUNT__, __GROUP__ and organization.
-// Each of them contains one required attribute and normal String attributes
-
-
-log.info("Entering " + action + " Script");
-
-idAIB = new AttributeInfoBuilder("id", String.class);
-idAIB.setRequired(true);
-
-orgAttrsInfo = new HashSet<AttributeInfo>();
-orgAttrsInfo.add(idAIB.build());
-orgAttrsInfo.add(AttributeInfoBuilder.build("location", String.class));
-// Create the organization Object class
-ObjectClassInfo ociOrg = new ObjectClassInfoBuilder().setType("__PRINTER__").addAllAttributeInfo(orgAttrsInfo).build();
-builder.defineObjectClass(ociOrg);
-
-log.info("Schema script done");
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/ec6bcf2f/fit/core-reference/src/main/resources/scriptedsql/SearchScript.groovy
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/scriptedsql/SearchScript.groovy b/fit/core-reference/src/main/resources/scriptedsql/SearchScript.groovy
deleted file mode 100644
index cc0dd65..0000000
--- a/fit/core-reference/src/main/resources/scriptedsql/SearchScript.groovy
+++ /dev/null
@@ -1,94 +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.
- */
-import groovy.sql.Sql;
-import groovy.sql.DataSet;
-
-// Parameters:
-// The connector sends the following:
-// connection: handler to the SQL connection
-// objectClass: a String describing the Object class (__ACCOUNT__ / __GROUP__ / other)
-// action: a string describing the action ("SEARCH" here)
-// log: a handler to the Log facility
-// options: a handler to the OperationOptions Map
-// query: a handler to the Query Map
-//
-// The Query map describes the filter used.
-//
-// query = [ operation: "CONTAINS", left: attribute, right: "value", not: true/false ]
-// query = [ operation: "ENDSWITH", left: attribute, right: "value", not: true/false ]
-// query = [ operation: "STARTSWITH", left: attribute, right: "value", not: true/false ]
-// query = [ operation: "EQUALS", left: attribute, right: "value", not: true/false ]
-// query = [ operation: "GREATERTHAN", left: attribute, right: "value", not: true/false ]
-// query = [ operation: "GREATERTHANOREQUAL", left: attribute, right: "value", not: true/false ]
-// query = [ operation: "LESSTHAN", left: attribute, right: "value", not: true/false ]
-// query = [ operation: "LESSTHANOREQUAL", left: attribute, right: "value", not: true/false ]
-// query = null : then we assume we fetch everything
-//
-// AND and OR filter just embed a left/right couple of queries.
-// query = [ operation: "AND", left: query1, right: query2 ]
-// query = [ operation: "OR", left: query1, right: query2 ]
-//
-// Returns: A list of Maps. Each map describing one row.
-// !!!! Each Map must contain a '__UID__' and '__NAME__' attribute.
-// This is required to build a ConnectorObject.
-
-log.info("Entering " + action + " Script");
-
-def sql = new Sql(connection);
-def result = []
-def where = "";
-
-if (query != null)  {
-  // Need to handle the __UID__ in queries
-  if (query.get("left").equalsIgnoreCase("__UID__") && objectClass.equalsIgnoreCase("__PRINTER__")) {
-    query.put("left","id")
-  }
-  
-  // We can use Groovy template engine to generate our custom SQL queries
-  def engine = new groovy.text.SimpleTemplateEngine();
-
-  def whereTemplates = [
-    CONTAINS:' WHERE $left ${not ? "NOT " : ""}LIKE "%$right%"',
-    ENDSWITH:' WHERE $left ${not ? "NOT " : ""}LIKE "%$right"',
-    STARTSWITH:' WHERE $left ${not ? "NOT " : ""}LIKE "$right%"',
-    EQUALS:' WHERE $left ${not ? "<>" : "="} \'$right\'',
-    GREATERTHAN:' WHERE $left ${not ? "<=" : ">"} "$right"',
-    GREATERTHANOREQUAL:' WHERE $left ${not ? "<" : ">="} "$right"',
-    LESSTHAN:' WHERE $left ${not ? ">=" : "<"} "$right"',
-    LESSTHANOREQUAL:' WHERE $left ${not ? ">" : "<="} "$right"'
-  ]
-
-  def wt = whereTemplates.get(query.get("operation"));
-  def binding = [left:query.get("left"),right:query.get("right"),not:query.get("not")];
-  def template = engine.createTemplate(wt).make(binding);
-  where = template.toString();
-  log.ok("Search WHERE clause is: "+ where)
-}
-
-switch ( objectClass ) {
-case "__PRINTER__":
-  sql.eachRow("SELECT * FROM TESTPRINTER " + where, 
-    {result.add([__UID__:it.id, __NAME__:it.id, location:it.location])} );
-  break
-
-default:
-  result;
-}
-
-return result;

http://git-wip-us.apache.org/repos/asf/syncope/blob/ec6bcf2f/fit/core-reference/src/main/resources/scriptedsql/SyncScript.groovy
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/scriptedsql/SyncScript.groovy b/fit/core-reference/src/main/resources/scriptedsql/SyncScript.groovy
deleted file mode 100644
index 6060870..0000000
--- a/fit/core-reference/src/main/resources/scriptedsql/SyncScript.groovy
+++ /dev/null
@@ -1,98 +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.
- */
-import groovy.sql.Sql;
-import groovy.sql.DataSet;
-
-// Parameters:
-// The connector sends the following:
-// connection: handler to the SQL connection
-// objectClass: a String describing the Object class (__ACCOUNT__ / __GROUP__ / other)
-// action: a string describing the action ("SYNC" or "GET_LATEST_SYNC_TOKEN" here)
-// log: a handler to the Log facility
-// options: a handler to the OperationOptions Map (null if action = "GET_LATEST_SYNC_TOKEN")
-// token: a handler to an Object representing the sync token (null if action = "GET_LATEST_SYNC_TOKEN")
-//
-//
-// Returns:
-// if action = "GET_LATEST_SYNC_TOKEN", it must return an object representing the last known
-// sync token for the corresponding ObjectClass
-// 
-// if action = "SYNC":
-// A list of Maps . Each map describing one update:
-// Map should look like the following:
-//
-// [
-// "token": <Object> token object (could be Integer, Date, String) , [!! could be null]
-// "operation":<String> ("CREATE_OR_UPDATE"|"DELETE")  will always default to CREATE_OR_DELETE ,
-// "uid":<String> uid  (uid of the entry) ,
-// "previousUid":<String> prevuid (This is for rename ops) ,
-// "password":<String> password (optional... allows to pass clear text password if needed),
-// "attributes":Map<String,List> of attributes name/values
-// ]
-
-log.info("Entering " + action + " Script");
-def sql = new Sql(connection);
-
-if (action.equalsIgnoreCase("GET_LATEST_SYNC_TOKEN")) {
-  switch (objectClass) {
-  case "__PRINTER__":
-    row = sql.firstRow("SELECT lastmodification FROM TESTPRINTER ORDER BY lastmodification DESC");
-    log.ok("Get Latest Sync Token script: last token is: " + row["lastmodification"])
-    break;
-    
-  default:
-    row = null;
-  }
-
-  return row == null ? null : row["lastmodification"].getTime();
-} else if (action.equalsIgnoreCase("SYNC")) {
-  def result = [];
-  def lastmodification = null;
-  if (token != null) {
-    lastmodification = new Date(token);
-  } else {
-    lastmodification = new Date(0);
-  }
-
-  switch (objectClass) {
-  case "__PRINTER__":    
-    sql.eachRow("SELECT * FROM TESTPRINTER WHERE lastmodification > ${lastmodification}",
-      {
-        result.add([
-            operation:"CREATE_OR_UPDATE", 
-            uid:it.id.toString(), 
-            token:it.lastmodification.getTime(), 
-            attributes:[
-              __UID__:it.id.toString(),
-              __NAME__:it.id.toString(),
-              id:it.id.toString(),
-              location:it.location
-            ]
-          ]);
-      }
-    )
-    break;
-  }
-  
-  log.ok("Sync script: found " + result.size() + " events to sync");
-  return result;
-} else {
-  log.error("Sync script: action '" + action + "' is not implemented in this script");
-  return null;
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ec6bcf2f/fit/core-reference/src/main/resources/scriptedsql/TestScript.groovy
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/scriptedsql/TestScript.groovy b/fit/core-reference/src/main/resources/scriptedsql/TestScript.groovy
deleted file mode 100644
index 5b8f4b5..0000000
--- a/fit/core-reference/src/main/resources/scriptedsql/TestScript.groovy
+++ /dev/null
@@ -1,31 +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.
- */
-import groovy.sql.Sql;
-import groovy.sql.DataSet;
-
-// Parameters:
-// The connector sends the following:
-// connection: handler to the SQL connection
-// action: a string describing the action ("TEST" here)
-// log: a handler to the Log facility
-
-log.info("Entering " + action + " Script");
-def sql = new Sql(connection);
-
-sql.eachRow("select * from TESTPRINTER", { println it.uid} );
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/ec6bcf2f/fit/core-reference/src/main/resources/scriptedsql/UpdateScript.groovy
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/resources/scriptedsql/UpdateScript.groovy b/fit/core-reference/src/main/resources/scriptedsql/UpdateScript.groovy
deleted file mode 100644
index f3155cb..0000000
--- a/fit/core-reference/src/main/resources/scriptedsql/UpdateScript.groovy
+++ /dev/null
@@ -1,67 +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.
- */
-import groovy.sql.Sql;
-import groovy.sql.DataSet;
-
-// Parameters:
-// The connector sends us the following:
-// connection : SQL connection
-//
-// action: String correponding to the action (UPDATE/ADD_ATTRIBUTE_VALUES/REMOVE_ATTRIBUTE_VALUES)
-//   - UPDATE : For each input attribute, replace all of the current values of that attribute
-//     in the target object with the values of that attribute.
-//   - ADD_ATTRIBUTE_VALUES: For each attribute that the input set contains, add to the current values
-//     of that attribute in the target object all of the values of that attribute in the input set.
-//   - REMOVE_ATTRIBUTE_VALUES: For each attribute that the input set contains, remove from the current values
-//     of that attribute in the target object any value that matches one of the values of the attribute from the input set.
-
-// log: a handler to the Log facility
-//
-// objectClass: a String describing the Object class (__ACCOUNT__ / __GROUP__ / other)
-//
-// uid: a String representing the entry uid
-//
-// attributes: an Attribute Map, containg the <String> attribute name as a key
-// and the <List> attribute value(s) as value.
-//
-// password: password string, clear text (only for UPDATE)
-//
-// options: a handler to the OperationOptions Map
-
-log.info("Entering " + action + " Script");
-def sql = new Sql(connection);
-
-
-switch (action) {
-case "UPDATE":
-  if (attributes.get("location").get(0) != null) {
-    sql.executeUpdate("UPDATE TESTPRINTER SET location = ?, lastmodification = ? where id = ?", 
-      [attributes.get("location").get(0), new Date(), attributes.get("__NAME__").get(0)])
-    
-    return attributes.get("__NAME__").get(0);
-  }
-  break
-
-case "ADD_ATTRIBUTE_VALUES":
-  break
-
-
-default:
-  sql
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/ec6bcf2f/fit/core-reference/src/test/resources/scriptedsql/CreateScript.groovy
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/resources/scriptedsql/CreateScript.groovy b/fit/core-reference/src/test/resources/scriptedsql/CreateScript.groovy
new file mode 100644
index 0000000..52c10f9
--- /dev/null
+++ b/fit/core-reference/src/test/resources/scriptedsql/CreateScript.groovy
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+import groovy.sql.Sql;
+import groovy.sql.DataSet;
+
+// Parameters:
+// The connector sends us the following:
+// connection : SQL connection
+// action: String correponding to the action ("CREATE" here)
+// log: a handler to the Log facility
+// objectClass: a String describing the Object class (__ACCOUNT__ / __GROUP__ / other)
+// id: The entry identifier (OpenICF "Name" atribute. (most often matches the uid)
+// attributes: an Attribute Map, containg the <String> attribute name as a key
+// and the <List> attribute value(s) as value.
+// password: password string, clear text
+// options: a handler to the OperationOptions Map
+
+log.info("Entering " + action + " Script");
+
+def sql = new Sql(connection);
+
+switch ( objectClass ) {
+case "__PRINTER__":
+  sql.execute("INSERT INTO TESTPRINTER (id, location, lastmodification) values (?,?,?)",
+    [
+      id,
+      attributes.get("location").get(0),
+      new Date()
+    ])
+  break
+
+default:
+  id;
+}
+
+return id;

http://git-wip-us.apache.org/repos/asf/syncope/blob/ec6bcf2f/fit/core-reference/src/test/resources/scriptedsql/DeleteScript.groovy
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/resources/scriptedsql/DeleteScript.groovy b/fit/core-reference/src/test/resources/scriptedsql/DeleteScript.groovy
new file mode 100644
index 0000000..cdd7f5b
--- /dev/null
+++ b/fit/core-reference/src/test/resources/scriptedsql/DeleteScript.groovy
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+import groovy.sql.Sql;
+import groovy.sql.DataSet;
+
+// Parameters:
+// The connector sends the following:
+// connection: handler to the SQL connection
+// action: a string describing the action ("DELETE" here)
+// log: a handler to the Log facility
+// objectClass: a String describing the Object class (__ACCOUNT__ / __GROUP__ / other)
+// options: a handler to the OperationOptions Map
+// uid: String for the unique id that specifies the object to delete
+
+log.info("Entering " + action + " Script");
+def sql = new Sql(connection);
+
+assert uid != null
+
+switch ( objectClass ) {
+case "__PRINTER__":
+  sql.execute("DELETE FROM TESTPRINTER where id= ?",[uid])
+  break
+
+default:
+  uid;
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ec6bcf2f/fit/core-reference/src/test/resources/scriptedsql/SchemaScript.groovy
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/resources/scriptedsql/SchemaScript.groovy b/fit/core-reference/src/test/resources/scriptedsql/SchemaScript.groovy
new file mode 100644
index 0000000..50f8a8a
--- /dev/null
+++ b/fit/core-reference/src/test/resources/scriptedsql/SchemaScript.groovy
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+import org.identityconnectors.framework.common.objects.AttributeInfo;
+import org.identityconnectors.framework.common.objects.AttributeInfo.Flags;
+import org.identityconnectors.framework.common.objects.AttributeInfoBuilder;
+import org.identityconnectors.framework.common.objects.ObjectClassInfo;
+import org.identityconnectors.framework.common.objects.ObjectClassInfoBuilder;
+
+// Parameters:
+// The connector sends the following:
+// action: a string describing the action ("SCHEMA" here)
+// log: a handler to the Log facility
+// builder: SchemaBuilder instance for the connector
+//
+// The connector will make the final call to builder.build()
+// so the scipt just need to declare the different object types.
+
+// This sample shows how to create 3 basic ObjectTypes: __ACCOUNT__, __GROUP__ and organization.
+// Each of them contains one required attribute and normal String attributes
+
+
+log.info("Entering " + action + " Script");
+
+idAIB = new AttributeInfoBuilder("id", String.class);
+idAIB.setRequired(true);
+
+orgAttrsInfo = new HashSet<AttributeInfo>();
+orgAttrsInfo.add(idAIB.build());
+orgAttrsInfo.add(AttributeInfoBuilder.build("location", String.class));
+// Create the organization Object class
+ObjectClassInfo ociOrg = new ObjectClassInfoBuilder().setType("__PRINTER__").addAllAttributeInfo(orgAttrsInfo).build();
+builder.defineObjectClass(ociOrg);
+
+log.info("Schema script done");
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/ec6bcf2f/fit/core-reference/src/test/resources/scriptedsql/SearchScript.groovy
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/resources/scriptedsql/SearchScript.groovy b/fit/core-reference/src/test/resources/scriptedsql/SearchScript.groovy
new file mode 100644
index 0000000..cc0dd65
--- /dev/null
+++ b/fit/core-reference/src/test/resources/scriptedsql/SearchScript.groovy
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+import groovy.sql.Sql;
+import groovy.sql.DataSet;
+
+// Parameters:
+// The connector sends the following:
+// connection: handler to the SQL connection
+// objectClass: a String describing the Object class (__ACCOUNT__ / __GROUP__ / other)
+// action: a string describing the action ("SEARCH" here)
+// log: a handler to the Log facility
+// options: a handler to the OperationOptions Map
+// query: a handler to the Query Map
+//
+// The Query map describes the filter used.
+//
+// query = [ operation: "CONTAINS", left: attribute, right: "value", not: true/false ]
+// query = [ operation: "ENDSWITH", left: attribute, right: "value", not: true/false ]
+// query = [ operation: "STARTSWITH", left: attribute, right: "value", not: true/false ]
+// query = [ operation: "EQUALS", left: attribute, right: "value", not: true/false ]
+// query = [ operation: "GREATERTHAN", left: attribute, right: "value", not: true/false ]
+// query = [ operation: "GREATERTHANOREQUAL", left: attribute, right: "value", not: true/false ]
+// query = [ operation: "LESSTHAN", left: attribute, right: "value", not: true/false ]
+// query = [ operation: "LESSTHANOREQUAL", left: attribute, right: "value", not: true/false ]
+// query = null : then we assume we fetch everything
+//
+// AND and OR filter just embed a left/right couple of queries.
+// query = [ operation: "AND", left: query1, right: query2 ]
+// query = [ operation: "OR", left: query1, right: query2 ]
+//
+// Returns: A list of Maps. Each map describing one row.
+// !!!! Each Map must contain a '__UID__' and '__NAME__' attribute.
+// This is required to build a ConnectorObject.
+
+log.info("Entering " + action + " Script");
+
+def sql = new Sql(connection);
+def result = []
+def where = "";
+
+if (query != null)  {
+  // Need to handle the __UID__ in queries
+  if (query.get("left").equalsIgnoreCase("__UID__") && objectClass.equalsIgnoreCase("__PRINTER__")) {
+    query.put("left","id")
+  }
+  
+  // We can use Groovy template engine to generate our custom SQL queries
+  def engine = new groovy.text.SimpleTemplateEngine();
+
+  def whereTemplates = [
+    CONTAINS:' WHERE $left ${not ? "NOT " : ""}LIKE "%$right%"',
+    ENDSWITH:' WHERE $left ${not ? "NOT " : ""}LIKE "%$right"',
+    STARTSWITH:' WHERE $left ${not ? "NOT " : ""}LIKE "$right%"',
+    EQUALS:' WHERE $left ${not ? "<>" : "="} \'$right\'',
+    GREATERTHAN:' WHERE $left ${not ? "<=" : ">"} "$right"',
+    GREATERTHANOREQUAL:' WHERE $left ${not ? "<" : ">="} "$right"',
+    LESSTHAN:' WHERE $left ${not ? ">=" : "<"} "$right"',
+    LESSTHANOREQUAL:' WHERE $left ${not ? ">" : "<="} "$right"'
+  ]
+
+  def wt = whereTemplates.get(query.get("operation"));
+  def binding = [left:query.get("left"),right:query.get("right"),not:query.get("not")];
+  def template = engine.createTemplate(wt).make(binding);
+  where = template.toString();
+  log.ok("Search WHERE clause is: "+ where)
+}
+
+switch ( objectClass ) {
+case "__PRINTER__":
+  sql.eachRow("SELECT * FROM TESTPRINTER " + where, 
+    {result.add([__UID__:it.id, __NAME__:it.id, location:it.location])} );
+  break
+
+default:
+  result;
+}
+
+return result;

http://git-wip-us.apache.org/repos/asf/syncope/blob/ec6bcf2f/fit/core-reference/src/test/resources/scriptedsql/SyncScript.groovy
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/resources/scriptedsql/SyncScript.groovy b/fit/core-reference/src/test/resources/scriptedsql/SyncScript.groovy
new file mode 100644
index 0000000..6060870
--- /dev/null
+++ b/fit/core-reference/src/test/resources/scriptedsql/SyncScript.groovy
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+import groovy.sql.Sql;
+import groovy.sql.DataSet;
+
+// Parameters:
+// The connector sends the following:
+// connection: handler to the SQL connection
+// objectClass: a String describing the Object class (__ACCOUNT__ / __GROUP__ / other)
+// action: a string describing the action ("SYNC" or "GET_LATEST_SYNC_TOKEN" here)
+// log: a handler to the Log facility
+// options: a handler to the OperationOptions Map (null if action = "GET_LATEST_SYNC_TOKEN")
+// token: a handler to an Object representing the sync token (null if action = "GET_LATEST_SYNC_TOKEN")
+//
+//
+// Returns:
+// if action = "GET_LATEST_SYNC_TOKEN", it must return an object representing the last known
+// sync token for the corresponding ObjectClass
+// 
+// if action = "SYNC":
+// A list of Maps . Each map describing one update:
+// Map should look like the following:
+//
+// [
+// "token": <Object> token object (could be Integer, Date, String) , [!! could be null]
+// "operation":<String> ("CREATE_OR_UPDATE"|"DELETE")  will always default to CREATE_OR_DELETE ,
+// "uid":<String> uid  (uid of the entry) ,
+// "previousUid":<String> prevuid (This is for rename ops) ,
+// "password":<String> password (optional... allows to pass clear text password if needed),
+// "attributes":Map<String,List> of attributes name/values
+// ]
+
+log.info("Entering " + action + " Script");
+def sql = new Sql(connection);
+
+if (action.equalsIgnoreCase("GET_LATEST_SYNC_TOKEN")) {
+  switch (objectClass) {
+  case "__PRINTER__":
+    row = sql.firstRow("SELECT lastmodification FROM TESTPRINTER ORDER BY lastmodification DESC");
+    log.ok("Get Latest Sync Token script: last token is: " + row["lastmodification"])
+    break;
+    
+  default:
+    row = null;
+  }
+
+  return row == null ? null : row["lastmodification"].getTime();
+} else if (action.equalsIgnoreCase("SYNC")) {
+  def result = [];
+  def lastmodification = null;
+  if (token != null) {
+    lastmodification = new Date(token);
+  } else {
+    lastmodification = new Date(0);
+  }
+
+  switch (objectClass) {
+  case "__PRINTER__":    
+    sql.eachRow("SELECT * FROM TESTPRINTER WHERE lastmodification > ${lastmodification}",
+      {
+        result.add([
+            operation:"CREATE_OR_UPDATE", 
+            uid:it.id.toString(), 
+            token:it.lastmodification.getTime(), 
+            attributes:[
+              __UID__:it.id.toString(),
+              __NAME__:it.id.toString(),
+              id:it.id.toString(),
+              location:it.location
+            ]
+          ]);
+      }
+    )
+    break;
+  }
+  
+  log.ok("Sync script: found " + result.size() + " events to sync");
+  return result;
+} else {
+  log.error("Sync script: action '" + action + "' is not implemented in this script");
+  return null;
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/ec6bcf2f/fit/core-reference/src/test/resources/scriptedsql/TestScript.groovy
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/resources/scriptedsql/TestScript.groovy b/fit/core-reference/src/test/resources/scriptedsql/TestScript.groovy
new file mode 100644
index 0000000..5b8f4b5
--- /dev/null
+++ b/fit/core-reference/src/test/resources/scriptedsql/TestScript.groovy
@@ -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.
+ */
+import groovy.sql.Sql;
+import groovy.sql.DataSet;
+
+// Parameters:
+// The connector sends the following:
+// connection: handler to the SQL connection
+// action: a string describing the action ("TEST" here)
+// log: a handler to the Log facility
+
+log.info("Entering " + action + " Script");
+def sql = new Sql(connection);
+
+sql.eachRow("select * from TESTPRINTER", { println it.uid} );
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/ec6bcf2f/fit/core-reference/src/test/resources/scriptedsql/UpdateScript.groovy
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/resources/scriptedsql/UpdateScript.groovy b/fit/core-reference/src/test/resources/scriptedsql/UpdateScript.groovy
new file mode 100644
index 0000000..f3155cb
--- /dev/null
+++ b/fit/core-reference/src/test/resources/scriptedsql/UpdateScript.groovy
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+import groovy.sql.Sql;
+import groovy.sql.DataSet;
+
+// Parameters:
+// The connector sends us the following:
+// connection : SQL connection
+//
+// action: String correponding to the action (UPDATE/ADD_ATTRIBUTE_VALUES/REMOVE_ATTRIBUTE_VALUES)
+//   - UPDATE : For each input attribute, replace all of the current values of that attribute
+//     in the target object with the values of that attribute.
+//   - ADD_ATTRIBUTE_VALUES: For each attribute that the input set contains, add to the current values
+//     of that attribute in the target object all of the values of that attribute in the input set.
+//   - REMOVE_ATTRIBUTE_VALUES: For each attribute that the input set contains, remove from the current values
+//     of that attribute in the target object any value that matches one of the values of the attribute from the input set.
+
+// log: a handler to the Log facility
+//
+// objectClass: a String describing the Object class (__ACCOUNT__ / __GROUP__ / other)
+//
+// uid: a String representing the entry uid
+//
+// attributes: an Attribute Map, containg the <String> attribute name as a key
+// and the <List> attribute value(s) as value.
+//
+// password: password string, clear text (only for UPDATE)
+//
+// options: a handler to the OperationOptions Map
+
+log.info("Entering " + action + " Script");
+def sql = new Sql(connection);
+
+
+switch (action) {
+case "UPDATE":
+  if (attributes.get("location").get(0) != null) {
+    sql.executeUpdate("UPDATE TESTPRINTER SET location = ?, lastmodification = ? where id = ?", 
+      [attributes.get("location").get(0), new Date(), attributes.get("__NAME__").get(0)])
+    
+    return attributes.get("__NAME__").get(0);
+  }
+  break
+
+case "ADD_ATTRIBUTE_VALUES":
+  break
+
+
+default:
+  sql
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/ec6bcf2f/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index d50da6b..a4df5a5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -321,7 +321,7 @@ under the License.
 
     <connid.version>1.4.1.0</connid.version>
     <connid.soap.version>1.3.0</connid.soap.version>
-    <connid.database.version>2.2.2</connid.database.version>
+    <connid.database.version>2.2.3-SNAPSHOT</connid.database.version>
     <connid.csvdir.version>0.8.2</connid.csvdir.version>
     <connid.ldap.version>1.4.0</connid.ldap.version>
     <connid.ad.version>1.2.3</connid.ad.version>

http://git-wip-us.apache.org/repos/asf/syncope/blob/ec6bcf2f/standalone/pom.xml
----------------------------------------------------------------------
diff --git a/standalone/pom.xml b/standalone/pom.xml
index 8976467..a23b810 100644
--- a/standalone/pom.xml
+++ b/standalone/pom.xml
@@ -36,6 +36,7 @@ under the License.
   <properties>
     <rootpom.basedir>${basedir}/..</rootpom.basedir>
 
+    <conf.directory>${CATALINA_HOME}/webapps/syncope/WEB-INF/classes</conf.directory>
     <connid.location>connid://${testconnectorserver.key}@localhost:${testconnectorserver.port}</connid.location>
     <log.directory>./logs</log.directory>
     <test.csvdir.path>./test-csvdir</test.csvdir.path>
@@ -94,7 +95,7 @@ under the License.
             <transformationSet>
               <dir>${project.build.directory}/classes/core</dir>
               <includes>
-                <include>content.xml</include>
+                <include>domains/MasterContent.xml</include>
               </includes>
               <outputDir>${project.build.directory}/classes/core</outputDir>
               <stylesheet>${basedir}/../fit/core-reference/src/test/resources/addActivitiToContent.xsl</stylesheet>
@@ -214,15 +215,19 @@ under the License.
 
     <resources>
       <resource>
-        <directory>../core/persistence-jpa/src/test/resources</directory>
+        <directory>../core/persistence-jpa/src/main/resources</directory>
         <includes>
-          <include>content.xml</include>
           <include>persistence.properties</include>
         </includes>
         <targetPath>core</targetPath>
         <filtering>true</filtering>
       </resource>
       <resource>
+        <directory>../core/persistence-jpa/src/test/resources/domains</directory>
+        <targetPath>core/domains</targetPath>
+        <filtering>true</filtering>
+      </resource>
+      <resource>
         <directory>../fit/core-reference/src/main/resources</directory>
         <includes>
           <include>*.xml</include>
@@ -240,6 +245,11 @@ under the License.
         <targetPath>core</targetPath>
         <filtering>true</filtering>
       </resource>
+      <resource>
+        <directory>../fit/core-reference/src/test/resources/scriptedsql</directory>
+        <targetPath>core/scriptedsql</targetPath>
+        <filtering>true</filtering>
+      </resource>
 
       <resource>
         <directory>../fit/console-reference/src/main/resources</directory>

http://git-wip-us.apache.org/repos/asf/syncope/blob/ec6bcf2f/standalone/src/main/resources/setenv.bat
----------------------------------------------------------------------
diff --git a/standalone/src/main/resources/setenv.bat b/standalone/src/main/resources/setenv.bat
index 2267b8b..9dc2c67 100644
--- a/standalone/src/main/resources/setenv.bat
+++ b/standalone/src/main/resources/setenv.bat
@@ -13,4 +13,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-set JAVA_OPTS=-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms2048m -Xmx2048m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:+DisableExplicitGC
+set JAVA_OPTS=-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms2048m -Xmx2048m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:+DisableExplicitGC -DCATALINA_HOME=%CATALINA_HOME%

http://git-wip-us.apache.org/repos/asf/syncope/blob/ec6bcf2f/standalone/src/main/resources/setenv.sh
----------------------------------------------------------------------
diff --git a/standalone/src/main/resources/setenv.sh b/standalone/src/main/resources/setenv.sh
index 22cff49..fbc725b 100755
--- a/standalone/src/main/resources/setenv.sh
+++ b/standalone/src/main/resources/setenv.sh
@@ -13,4 +13,4 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms2048m -Xmx2048m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:+DisableExplicitGC"
+JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms2048m -Xmx2048m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:+DisableExplicitGC -DCATALINA_HOME=$CATALINA_HOME"


[31/33] syncope git commit: Upgrading Tomcat

Posted by md...@apache.org.
Upgrading Tomcat


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

Branch: refs/heads/SYNCOPE-156
Commit: 39eb5756b0da87327109322b6404cb0b14d1d995
Parents: a1737d3
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Wed Aug 26 17:39:16 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Wed Aug 26 17:39:16 2015 +0200

----------------------------------------------------------------------
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/39eb5756/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index a952b3c..6cee0c5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -375,7 +375,7 @@ under the License.
     
     <httpclient.version>4.3.6</httpclient.version>
 
-    <tomcat.version>7.0.63</tomcat.version>
+    <tomcat.version>7.0.64</tomcat.version>
 
     <jasypt.version>1.9.2</jasypt.version>
  


[21/33] syncope git commit: Upgrading Camel

Posted by md...@apache.org.
Upgrading Camel


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

Branch: refs/heads/SYNCOPE-156
Commit: 966484b4c07d19c1bce3ddf6b2103639969306f5
Parents: 56fe1ee
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Mon Aug 24 11:55:17 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Mon Aug 24 11:55:17 2015 +0200

----------------------------------------------------------------------
 pom.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/966484b4/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index a4df5a5..fb27973 100644
--- a/pom.xml
+++ b/pom.xml
@@ -328,7 +328,7 @@ under the License.
 
     <cxf.version>3.1.2</cxf.version>
 
-    <camel.version>2.15.2</camel.version>	
+    <camel.version>2.15.3</camel.version>	
 
     <jackson.version>2.6.1</jackson.version>
 


[02/33] syncope git commit: Cleanup unused stuff

Posted by md...@apache.org.
Cleanup unused stuff


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

Branch: refs/heads/SYNCOPE-156
Commit: fc91e516ed0ba34f8252830f69830460d58eae6b
Parents: 8fe3c7c
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Mon Aug 17 17:28:55 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Mon Aug 17 17:28:55 2015 +0200

----------------------------------------------------------------------
 .../lib/annotation/FormAttributeField.java      | 36 --------------------
 .../lib/report/AbstractAnyReportletConf.java    |  6 ----
 .../core/misc/policy/AccountPolicyEnforcer.java |  9 ++---
 .../misc/policy/PasswordPolicyEnforcer.java     | 17 ++++-----
 .../misc/policy/PolicyEnforceException.java     | 32 -----------------
 .../core/misc/policy/PolicyEvaluator.java       | 13 +++----
 .../core/persistence/jpa/dao/JPAUserDAO.java    |  4 +--
 7 files changed, 15 insertions(+), 102 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/fc91e516/common/lib/src/main/java/org/apache/syncope/common/lib/annotation/FormAttributeField.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/annotation/FormAttributeField.java b/common/lib/src/main/java/org/apache/syncope/common/lib/annotation/FormAttributeField.java
deleted file mode 100644
index 5d2603f..0000000
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/annotation/FormAttributeField.java
+++ /dev/null
@@ -1,36 +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.common.lib.annotation;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import org.apache.syncope.common.lib.types.IntMappingType;
-
-@Target({ ElementType.FIELD })
-@Retention(RetentionPolicy.RUNTIME)
-public @interface FormAttributeField {
-
-    boolean userSearch() default false;
-
-    boolean groupSearch() default false;
-
-    IntMappingType schema() default IntMappingType.UserPlainSchema;
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/fc91e516/common/lib/src/main/java/org/apache/syncope/common/lib/report/AbstractAnyReportletConf.java
----------------------------------------------------------------------
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/report/AbstractAnyReportletConf.java b/common/lib/src/main/java/org/apache/syncope/common/lib/report/AbstractAnyReportletConf.java
index e6949b6..29f6363 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/report/AbstractAnyReportletConf.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/report/AbstractAnyReportletConf.java
@@ -24,24 +24,18 @@ import java.util.List;
 import javax.xml.bind.annotation.XmlElement;
 import javax.xml.bind.annotation.XmlElementWrapper;
 import javax.xml.bind.annotation.XmlType;
-import org.apache.syncope.common.lib.annotation.FormAttributeField;
-import org.apache.syncope.common.lib.types.IntMappingType;
 
 @XmlType
 public abstract class AbstractAnyReportletConf extends AbstractReportletConf {
 
     private static final long serialVersionUID = -5388597116592877789L;
 
-    @FormAttributeField(userSearch = true)
     protected String matchingCond;
 
-    @FormAttributeField(schema = IntMappingType.UserPlainSchema)
     protected final List<String> plainAttrs = new ArrayList<>();
 
-    @FormAttributeField(schema = IntMappingType.UserDerivedSchema)
     protected final List<String> derAttrs = new ArrayList<>();
 
-    @FormAttributeField(schema = IntMappingType.UserVirtualSchema)
     protected final List<String> virAttrs = new ArrayList<>();
 
     public AbstractAnyReportletConf() {

http://git-wip-us.apache.org/repos/asf/syncope/blob/fc91e516/core/misc/src/main/java/org/apache/syncope/core/misc/policy/AccountPolicyEnforcer.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/policy/AccountPolicyEnforcer.java b/core/misc/src/main/java/org/apache/syncope/core/misc/policy/AccountPolicyEnforcer.java
index 23740d6..8b7a547 100644
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/policy/AccountPolicyEnforcer.java
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/policy/AccountPolicyEnforcer.java
@@ -20,7 +20,6 @@ package org.apache.syncope.core.misc.policy;
 
 import java.util.regex.Pattern;
 import org.apache.syncope.common.lib.types.AccountPolicySpec;
-import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.springframework.stereotype.Component;
 
@@ -29,13 +28,9 @@ public class AccountPolicyEnforcer {
 
     private static final Pattern DEFAULT_PATTERN = Pattern.compile("[a-zA-Z0-9-_@. ]+");
 
-    public boolean enforce(final AccountPolicySpec policy, final PolicyType type, final User user) {
+    public boolean enforce(final AccountPolicySpec policy, final User user) {
         if (user.getUsername() == null) {
-            throw new PolicyEnforceException("Invalid account");
-        }
-
-        if (policy == null) {
-            throw new PolicyEnforceException("Invalid policy");
+            throw new AccountPolicyException("Invalid account");
         }
 
         // check min length

http://git-wip-us.apache.org/repos/asf/syncope/blob/fc91e516/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PasswordPolicyEnforcer.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PasswordPolicyEnforcer.java b/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PasswordPolicyEnforcer.java
index 1458bf3..c2af367 100644
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PasswordPolicyEnforcer.java
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PasswordPolicyEnforcer.java
@@ -19,24 +19,21 @@
 package org.apache.syncope.core.misc.policy;
 
 import org.apache.syncope.common.lib.types.PasswordPolicySpec;
-import org.apache.syncope.common.lib.types.PolicyType;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.springframework.stereotype.Component;
 
 @Component
 public class PasswordPolicyEnforcer {
 
-    public boolean enforce(final PasswordPolicySpec policy, final PolicyType type, final User user) {
-        final String clearPassword = user.getClearPassword();
-        final String password = user.getPassword();
-
-        if (policy == null) {
-            throw new PolicyEnforceException("Invalid policy");
-        }
+    public boolean enforce(final PasswordPolicySpec policy, final User user) {
+        String clearPassword = user.getClearPassword();
+        String password = user.getPassword();
 
         if (password == null && !policy.isAllowNullPassword()) {
-            throw new PolicyEnforceException("Password mandatory");
-        } else if (password != null && clearPassword != null) {
+            throw new PasswordPolicyException("Password mandatory");
+        }
+        
+        if (password != null && clearPassword != null) {
             // check length
             if (policy.getMinLength() > 0 && policy.getMinLength() > clearPassword.length()) {
                 throw new PasswordPolicyException("Password too short");

http://git-wip-us.apache.org/repos/asf/syncope/blob/fc91e516/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PolicyEnforceException.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PolicyEnforceException.java b/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PolicyEnforceException.java
deleted file mode 100644
index e7211b8..0000000
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PolicyEnforceException.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.syncope.core.misc.policy;
-
-public class PolicyEnforceException extends PolicyException {
-
-    private static final long serialVersionUID = 3247084727383061069L;
-
-    public PolicyEnforceException() {
-        super();
-    }
-
-    public PolicyEnforceException(final String message) {
-        super(message);
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/fc91e516/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PolicyEvaluator.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PolicyEvaluator.java b/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PolicyEvaluator.java
index fd6abd2..425568d 100644
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PolicyEvaluator.java
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/policy/PolicyEvaluator.java
@@ -38,11 +38,7 @@ public class PolicyEvaluator {
 
     @SuppressWarnings("unchecked")
     public <T extends PolicySpec> T evaluate(final Policy policy, final Any<?, ?, ?> any) {
-        if (policy == null) {
-            return null;
-        }
-
-        T result = null;
+        T result;
         switch (policy.getType()) {
             case PASSWORD:
                 PasswordPolicySpec ppSpec = policy.getSpecification(PasswordPolicySpec.class);
@@ -75,13 +71,13 @@ public class PolicyEvaluator {
                 break;
 
             case ACCOUNT:
-                final AccountPolicySpec spec = policy.getSpecification(AccountPolicySpec.class);
-                final AccountPolicySpec accountPolicy = new AccountPolicySpec();
+                AccountPolicySpec spec = policy.getSpecification(AccountPolicySpec.class);
+                AccountPolicySpec accountPolicy = new AccountPolicySpec();
 
                 BeanUtils.copyProperties(spec, accountPolicy, new String[] { "schemasNotPermitted" });
 
                 for (String schema : spec.getSchemasNotPermitted()) {
-                    PlainAttr attr = any.getPlainAttr(schema);
+                    PlainAttr<?> attr = any.getPlainAttr(schema);
                     if (attr != null) {
                         List<String> values = attr.getValuesAsStrings();
                         if (values != null && !values.isEmpty()) {
@@ -93,7 +89,6 @@ public class PolicyEvaluator {
                 result = (T) accountPolicy;
                 break;
 
-            case SYNC:
             default:
                 result = null;
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/fc91e516/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
index 4e9afe1..7dd3fe7 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAUserDAO.java
@@ -239,7 +239,7 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
                 // evaluate policy
                 PasswordPolicySpec ppSpec = evaluator.evaluate(policy, user);
                 // enforce policy
-                ppEnforcer.enforce(ppSpec, policy.getType(), user);
+                ppEnforcer.enforce(ppSpec, user);
 
                 if (ppSpec.getHistoryLength() > maxPPSpecHistory) {
                     maxPPSpecHistory = ppSpec.getHistoryLength();
@@ -282,7 +282,7 @@ public class JPAUserDAO extends AbstractAnyDAO<User> implements UserDAO {
                 AccountPolicySpec apSpec = evaluator.evaluate(policy, user);
 
                 // enforce policy
-                suspend |= apEnforcer.enforce(apSpec, policy.getType(), user);
+                suspend |= apEnforcer.enforce(apSpec, user);
                 propagateSuspension |= apSpec.isPropagateSuspension();
             }
         } catch (Exception e) {


[33/33] syncope git commit: Merge branch 'master' into SYNCOPE-156

Posted by md...@apache.org.
Merge branch 'master' into SYNCOPE-156


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

Branch: refs/heads/SYNCOPE-156
Commit: d9437d37a38d0f185289b1c61d2d79c5ce8edaaa
Parents: bbb94a9 e545928
Author: Marco Di Sabatino Di Diodoro <md...@apache.org>
Authored: Thu Aug 27 13:11:57 2015 +0200
Committer: Marco Di Sabatino Di Diodoro <md...@apache.org>
Committed: Thu Aug 27 13:11:57 2015 +0200

----------------------------------------------------------------------
 README.md                                       |  24 ++
 archetype/pom.xml                               |  18 +-
 .../META-INF/maven/archetype-metadata.xml       |  10 +
 .../archetype-resources/common/pom.xml          |  60 ++++
 .../archetype-resources/console/pom.xml         |  43 ++-
 .../resources/archetype-resources/core/pom.xml  |   6 +
 .../main/resources/archetype-resources/pom.xml  |  26 +-
 archetype/src/main/resources/meta-pom.xml       |  26 +-
 .../client/cli/commands/PolicyCommand.java      |   2 +-
 .../client/cli/commands/ReportCommand.java      |  14 -
 .../console/commons/status/StatusUtils.java     |   2 +-
 .../console/panels/ResourceSecurityPanel.java   |   2 +-
 .../console/rest/ConnectorRestClient.java       |  16 +-
 .../client/console/rest/GroupRestClient.java    |  15 +-
 .../client/console/rest/PolicyRestClient.java   |   2 +-
 .../client/console/rest/ReportRestClient.java   |  18 -
 .../client/console/rest/UserRestClient.java     |  15 +-
 .../client/lib/SyncopeClientFactoryBean.java    |   2 +-
 .../lib/annotation/FormAttributeField.java      |  36 --
 .../syncope/common/lib/mod/StatusMod.java       |   2 +-
 .../lib/policy/AbstractAccountRuleConf.java     |  52 +++
 .../lib/policy/AbstractPasswordRuleConf.java    |  52 +++
 .../common/lib/policy/AbstractPolicyTO.java     |  97 ++++++
 .../common/lib/policy/AccountPolicyTO.java      |  77 +++++
 .../common/lib/policy/AccountRuleConf.java      |  23 ++
 .../lib/policy/DefaultAccountRuleConf.java      | 147 ++++++++
 .../lib/policy/DefaultPasswordRuleConf.java     | 342 +++++++++++++++++++
 .../common/lib/policy/PasswordPolicyTO.java     |  68 ++++
 .../common/lib/policy/PasswordRuleConf.java     |  23 ++
 .../syncope/common/lib/policy/PolicyType.java   |  43 +++
 .../common/lib/policy/PushPolicySpec.java       |  28 ++
 .../syncope/common/lib/policy/RuleConf.java     |  32 ++
 .../common/lib/policy/SyncPolicySpec.java       |  63 ++++
 .../syncope/common/lib/policy/SyncPolicyTO.java |  45 +++
 .../lib/report/AbstractAnyReportletConf.java    |   6 -
 .../common/lib/report/StaticReportletConf.java  |   2 +-
 .../syncope/common/lib/to/AbstractPolicyTO.java |  97 ------
 .../syncope/common/lib/to/AccountPolicyTO.java  |  59 ----
 .../syncope/common/lib/to/PasswordPolicyTO.java |  46 ---
 .../syncope/common/lib/to/SyncPolicyTO.java     |  46 ---
 .../apache/syncope/common/lib/to/SyncopeTO.java |  28 ++
 .../common/lib/types/AbstractPolicySpec.java    | 100 ------
 .../common/lib/types/AccountPolicySpec.java     | 121 -------
 .../common/lib/types/EntityViolationType.java   |   3 -
 .../common/lib/types/PasswordPolicySpec.java    | 300 ----------------
 .../syncope/common/lib/types/PolicySpec.java    |  25 --
 .../syncope/common/lib/types/PolicyType.java    |  43 ---
 .../common/lib/types/SyncPolicySpec.java        |  62 ----
 .../common/lib/wrap/ReportletConfClass.java     |  30 --
 .../org/apache/syncope/common/lib/JAXBTest.java |  42 +++
 .../rest/api/service/ConnectorService.java      |   9 +-
 .../common/rest/api/service/PolicyService.java  |   2 +-
 .../common/rest/api/service/ReportService.java  |  11 -
 .../syncope/core/logic/ConnectorLogic.java      |  64 ++--
 .../apache/syncope/core/logic/PolicyLogic.java  |  14 +-
 .../apache/syncope/core/logic/ReportLogic.java  |  75 ----
 .../apache/syncope/core/logic/SyncopeLogic.java |  32 +-
 .../apache/syncope/core/logic/UserLogic.java    |   2 +-
 .../init/ClassPathScanImplementationLookup.java | 200 +++++++++++
 .../init/ImplementationClassNamesLoader.java    | 144 --------
 .../core/logic/report/AbstractReportlet.java    |  22 +-
 .../core/logic/report/GroupReportlet.java       |  14 +-
 .../core/logic/report/ReportJobDelegate.java    |  33 +-
 .../syncope/core/logic/report/Reportlet.java    |  46 ---
 .../core/logic/report/ReportletConfClass.java   |  32 --
 .../core/logic/report/StaticReportlet.java      |  39 ++-
 .../core/logic/report/UserReportlet.java        |  14 +-
 .../syncope/core/misc/ConnObjectUtils.java      |  24 +-
 .../apache/syncope/core/misc/MappingUtils.java  |   6 +-
 .../core/misc/policy/AccountPolicyEnforcer.java |  90 -----
 .../InvalidPasswordPolicySpecException.java     |  37 --
 .../misc/policy/InvalidPasswordRuleConf.java    |  37 ++
 .../misc/policy/PasswordPolicyEnforcer.java     | 196 -----------
 .../misc/policy/PolicyEnforceException.java     |  32 --
 .../core/misc/policy/PolicyEvaluator.java       | 103 ------
 .../misc/security/DefaultPasswordGenerator.java | 334 ++++++++++++++++++
 .../core/misc/security/PasswordGenerator.java   | 310 +----------------
 .../misc/src/main/resources/security.properties |   1 +
 .../misc/src/main/resources/securityContext.xml |   1 +
 .../misc/security/PasswordGeneratorTest.java    | 146 ++++----
 .../persistence/api/ImplementationLookup.java   |  53 +++
 .../core/persistence/api/dao/AccountRule.java   |  30 ++
 .../api/dao/AccountRuleConfClass.java           |  32 ++
 .../core/persistence/api/dao/PasswordRule.java  |  30 ++
 .../api/dao/PasswordRuleConfClass.java          |  32 ++
 .../core/persistence/api/dao/PolicyDAO.java     |   2 +-
 .../core/persistence/api/dao/Reportlet.java     |  40 +++
 .../persistence/api/dao/ReportletConfClass.java |  32 ++
 .../persistence/api/entity/AccountPolicy.java   |  33 --
 .../persistence/api/entity/PasswordPolicy.java  |  23 --
 .../core/persistence/api/entity/Policy.java     |   5 -
 .../core/persistence/api/entity/PushPolicy.java |  23 --
 .../core/persistence/api/entity/Realm.java      |   3 +
 .../core/persistence/api/entity/Report.java     |  15 +-
 .../api/entity/ReportletConfInstance.java       |  33 --
 .../core/persistence/api/entity/SyncPolicy.java |  23 --
 .../api/entity/policy/AccountPolicy.java        |  50 +++
 .../api/entity/policy/PasswordPolicy.java       |  40 +++
 .../api/entity/policy/PushPolicy.java           |  29 ++
 .../api/entity/policy/SyncPolicy.java           |  30 ++
 .../api/entity/resource/ExternalResource.java   |   6 +-
 .../persistence/jpa/dao/DefaultAccountRule.java | 109 ++++++
 .../jpa/dao/DefaultPasswordRule.java            | 221 ++++++++++++
 .../jpa/dao/JPAExternalResourceDAO.java         |   2 +-
 .../core/persistence/jpa/dao/JPAPolicyDAO.java  |  14 +-
 .../core/persistence/jpa/dao/JPARealmDAO.java   |   2 +-
 .../persistence/jpa/dao/JPAReportExecDAO.java   |   2 +-
 .../core/persistence/jpa/dao/JPATaskDAO.java    |   4 +-
 .../core/persistence/jpa/dao/JPAUserDAO.java    | 119 ++++---
 .../jpa/entity/JPAAccountPolicy.java            |  84 -----
 .../jpa/entity/JPAEntityFactory.java            |  15 +-
 .../jpa/entity/JPAPasswordPolicy.java           |  36 --
 .../core/persistence/jpa/entity/JPAPolicy.java  |  90 -----
 .../persistence/jpa/entity/JPAPushPolicy.java   |  39 ---
 .../core/persistence/jpa/entity/JPARealm.java   |   6 +-
 .../core/persistence/jpa/entity/JPAReport.java  |  33 +-
 .../jpa/entity/JPAReportletConfInstance.java    |   7 +-
 .../persistence/jpa/entity/JPASyncPolicy.java   |  36 --
 .../jpa/entity/policy/AbstractPolicy.java       |  67 ++++
 .../jpa/entity/policy/JPAAccountPolicy.java     | 153 +++++++++
 .../policy/JPAAccountRuleConfInstance.java      |  73 ++++
 .../jpa/entity/policy/JPAPasswordPolicy.java    | 108 ++++++
 .../policy/JPAPasswordRuleConfInstance.java     |  73 ++++
 .../jpa/entity/policy/JPAPushPolicy.java        |  55 +++
 .../jpa/entity/policy/JPASyncPolicy.java        |  54 +++
 .../entity/resource/JPAExternalResource.java    |  12 +-
 .../jpa/entity/task/AbstractTask.java           |  87 +++++
 .../jpa/entity/task/JPANotificationTask.java    |   2 +-
 .../jpa/entity/task/JPAPropagationTask.java     |   2 +-
 .../jpa/entity/task/JPASchedTask.java           |   2 +-
 .../persistence/jpa/entity/task/JPATask.java    |  96 ------
 .../jpa/entity/task/JPATaskExec.java            |  11 +-
 .../jpa/validation/entity/PolicyCheck.java      |  41 ---
 .../jpa/validation/entity/PolicyValidator.java  |  59 ----
 .../resources/META-INF/spring-orm-oracle.xml    |  22 +-
 .../resources/META-INF/spring-orm-sqlserver.xml |  22 +-
 .../src/main/resources/META-INF/spring-orm.xml  |  22 +-
 .../jpa/DummyImplementationLookup.java          |  73 ++++
 .../core/persistence/jpa/inner/PolicyTest.java  |  46 +--
 .../core/persistence/jpa/inner/RealmTest.java   |   4 +-
 .../core/persistence/jpa/inner/ReportTest.java  |   4 +-
 .../core/persistence/jpa/inner/UserTest.java    |   6 +-
 .../core/persistence/jpa/outer/ReportTest.java  |   2 +-
 .../persistence/jpa/outer/ResourceTest.java     |   2 +-
 .../test/resources/domains/MasterContent.xml    |  54 +--
 .../src/test/resources/domains/Two.properties   |   2 +-
 .../src/test/resources/persistenceTest.xml      |   2 +
 .../provisioning/api/data/PolicyDataBinder.java |   2 +-
 .../java/ConnIdBundleManagerImpl.java           |  12 +-
 .../java/DefaultUserProvisioningManager.java    |   4 +-
 .../java/data/PolicyDataBinderImpl.java         | 100 ++++--
 .../java/data/RealmDataBinderImpl.java          |   4 +-
 .../java/data/ReportDataBinderImpl.java         |  35 +-
 .../java/data/ResourceDataBinderImpl.java       |   6 +-
 .../java/data/UserDataBinderImpl.java           |  40 +++
 .../java/job/SchedulerShutdown.java             |  49 +++
 .../propagation/PropagationManagerImpl.java     |   2 +-
 .../provisioning/java/sync/SyncJobDelegate.java |   4 +-
 .../core/provisioning/java/sync/SyncUtils.java  |   4 +-
 .../src/main/resources/provisioningContext.xml  |   1 +
 .../java/DummyImplementationLookup.java         |  73 ++++
 .../rest/cxf/service/ConnectorServiceImpl.java  |   8 +-
 .../rest/cxf/service/PolicyServiceImpl.java     |   8 +-
 .../rest/cxf/service/ReportServiceImpl.java     |   7 -
 .../activiti/ActivitiUserWorkflowAdapter.java   |   4 +-
 .../camel/processor/UserProvisionProcessor.java |   2 +-
 .../UserStatusPropagationProcessor.java         |   2 +-
 .../fit/buildtools/H2StartStopListener.java     |   3 -
 .../fit/core/reference/TestAccountRule.java     |  48 +++
 .../fit/core/reference/TestAccountRuleConf.java |  37 ++
 .../fit/core/reference/TestPasswordRule.java    |  48 +++
 .../core/reference/TestPasswordRuleConf.java    |  37 ++
 .../src/main/resources/log4j2.xml               |   2 +-
 .../resources/scriptedsql/CreateScript.groovy   |  52 ---
 .../resources/scriptedsql/DeleteScript.groovy   |  43 ---
 .../resources/scriptedsql/SchemaScript.groovy   |  50 ---
 .../resources/scriptedsql/SearchScript.groovy   |  94 -----
 .../resources/scriptedsql/SyncScript.groovy     |  98 ------
 .../resources/scriptedsql/TestScript.groovy     |  31 --
 .../resources/scriptedsql/UpdateScript.groovy   |  67 ----
 .../fit/core/reference/AbstractITCase.java      |   5 +-
 .../fit/core/reference/ConnectorITCase.java     |  51 +--
 .../fit/core/reference/MultitenancyITCase.java  |   3 +-
 .../fit/core/reference/PolicyITCase.java        |  51 ++-
 .../syncope/fit/core/reference/RealmITCase.java |  12 +-
 .../fit/core/reference/ReportITCase.java        |   7 +-
 .../fit/core/reference/SyncTaskITCase.java      |   8 +-
 .../syncope/fit/core/reference/UserITCase.java  | 233 ++++++++++---
 .../fit/core/reference/UserSelfITCase.java      |   2 +-
 .../fit/core/reference/VirAttrITCase.java       |  16 +-
 .../resources/scriptedsql/CreateScript.groovy   |  52 +++
 .../resources/scriptedsql/DeleteScript.groovy   |  43 +++
 .../resources/scriptedsql/SchemaScript.groovy   |  50 +++
 .../resources/scriptedsql/SearchScript.groovy   |  94 +++++
 .../resources/scriptedsql/SyncScript.groovy     |  98 ++++++
 .../resources/scriptedsql/TestScript.groovy     |  31 ++
 .../resources/scriptedsql/UpdateScript.groovy   |  67 ++++
 pom.xml                                         |  10 +-
 standalone/pom.xml                              |  16 +-
 standalone/src/main/resources/setenv.bat        |   2 +-
 standalone/src/main/resources/setenv.sh         |   2 +-
 201 files changed, 5099 insertions(+), 3781 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/d9437d37/client/console/src/main/java/org/apache/syncope/client/console/rest/GroupRestClient.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/syncope/blob/d9437d37/client/console/src/main/java/org/apache/syncope/client/console/rest/UserRestClient.java
----------------------------------------------------------------------


[23/33] syncope git commit: Upgrading Tomcat and CodeMirror

Posted by md...@apache.org.
Upgrading Tomcat and CodeMirror


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

Branch: refs/heads/SYNCOPE-156
Commit: 3559e694b91667da1ca474cecd207740a05bb127
Parents: 5da611d
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Wed Aug 26 08:46:41 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Wed Aug 26 08:46:41 2015 +0200

----------------------------------------------------------------------
 pom.xml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/3559e694/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index fb27973..adc785d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -379,7 +379,7 @@ under the License.
     <font-awesome.version>4.4.0</font-awesome.version>
     <ionicons.version>2.0.1</ionicons.version>
     <highlightjs.version>8.7</highlightjs.version>
-    <codemirror.version>5.5</codemirror.version>
+    <codemirror.version>5.6</codemirror.version>
     <jsplumb.version>1.7.5</jsplumb.version>
     
     <wicket.version>7.0.0</wicket.version>
@@ -408,7 +408,7 @@ under the License.
     <cargo.log>${log.directory}/cargo.log</cargo.log>
     <cargo.output>${log.directory}/cargo-output.log</cargo.output>
 
-    <tomcat.version>8.0.24</tomcat.version>
+    <tomcat.version>8.0.26</tomcat.version>
 
     <anonymousUser>anonymous</anonymousUser>
     <!-- static keys, only used for build: generated overlays will override during archetype:generate -->


[18/33] syncope git commit: Fixing archetype after SYNCOPE-620

Posted by md...@apache.org.
Fixing archetype after SYNCOPE-620


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

Branch: refs/heads/SYNCOPE-156
Commit: 213fbc6077b69d13965963b19c941af0a4878856
Parents: 69d8b7b
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Fri Aug 21 10:21:32 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Fri Aug 21 10:21:32 2015 +0200

----------------------------------------------------------------------
 archetype/pom.xml                               | 18 ++++--
 .../META-INF/maven/archetype-metadata.xml       | 10 +++
 .../archetype-resources/common/pom.xml          | 60 ++++++++++++++++++
 .../archetype-resources/console/pom.xml         | 42 ++++++++++++-
 .../resources/archetype-resources/core/pom.xml  |  6 ++
 .../main/resources/archetype-resources/pom.xml  | 26 +++++++-
 archetype/src/main/resources/meta-pom.xml       | 26 +++++++-
 .../console/rest/ConnectorRestClient.java       | 16 ++---
 .../rest/api/service/ConnectorService.java      |  9 ++-
 .../syncope/core/logic/ConnectorLogic.java      | 64 ++++++++++----------
 .../test/resources/domains/MasterContent.xml    |  2 +-
 .../java/ConnIdBundleManagerImpl.java           | 12 ++--
 .../rest/cxf/service/ConnectorServiceImpl.java  |  8 +--
 .../fit/buildtools/H2StartStopListener.java     |  3 -
 .../fit/core/reference/AbstractITCase.java      |  3 +-
 .../fit/core/reference/ConnectorITCase.java     | 51 ++++++++--------
 .../fit/core/reference/MultitenancyITCase.java  |  3 +-
 .../fit/core/reference/SyncTaskITCase.java      |  4 +-
 .../fit/core/reference/VirAttrITCase.java       | 12 ++--
 19 files changed, 280 insertions(+), 95 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/213fbc60/archetype/pom.xml
----------------------------------------------------------------------
diff --git a/archetype/pom.xml b/archetype/pom.xml
index 451ae8f..f22303b 100644
--- a/archetype/pom.xml
+++ b/archetype/pom.xml
@@ -142,11 +142,8 @@ under the License.
         <targetPath>${project.build.outputDirectory}/archetype-resources/core/src/main/resources</targetPath>
       </resource>
       <resource>
-        <directory>../core/persistence-jpa/src/test/resources</directory>
-        <includes>
-          <include>persistence.properties</include>
-        </includes>
-        <targetPath>${project.build.outputDirectory}/archetype-resources/core/src/test/resources</targetPath>
+        <directory>../core/persistence-jpa/src/test/resources/domains</directory>
+        <targetPath>${project.build.outputDirectory}/archetype-resources/core/src/test/resources/domains</targetPath>
       </resource>
       <resource>
         <directory>../core/logic/src/main/resources</directory>
@@ -178,6 +175,17 @@ under the License.
         </includes>
       </resource>
       <resource>
+        <directory>../fit/core-reference/src/main/resources/scriptedsql</directory>
+        <targetPath>${project.build.outputDirectory}/archetype-resources/core/src/main/resources/scriptedsql</targetPath>
+      </resource>
+      <resource>
+        <directory>../fit/core-reference/src/main/resources</directory>
+        <targetPath>${project.build.outputDirectory}/archetype-resources/core/src/test/resources</targetPath>
+        <includes>
+          <include>connid.properties</include>
+        </includes>
+      </resource>
+      <resource>
         <directory>../fit/core-reference/src/main/webapp</directory>
         <targetPath>${project.build.outputDirectory}/archetype-resources/core/src/test/resources</targetPath>
         <includes>

http://git-wip-us.apache.org/repos/asf/syncope/blob/213fbc60/archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
----------------------------------------------------------------------
diff --git a/archetype/src/main/resources/META-INF/maven/archetype-metadata.xml b/archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
index 44f3cfb..99d107b 100644
--- a/archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
+++ b/archetype/src/main/resources/META-INF/maven/archetype-metadata.xml
@@ -26,6 +26,16 @@ under the License.
   </requiredProperties>
   
   <modules>
+    <module id="common" dir="common" name="common">
+      <fileSets>
+        <fileSet filtered="false" encoding="UTF-8">
+          <directory>src/main/resources</directory>
+        </fileSet>
+        <fileSet filtered="false" encoding="UTF-8">
+          <directory>src/test/resources</directory>
+        </fileSet>
+      </fileSets>
+    </module>
     <module id="core" dir="core" name="core">
       <fileSets>
         <fileSet filtered="false" encoding="UTF-8">

http://git-wip-us.apache.org/repos/asf/syncope/blob/213fbc60/archetype/src/main/resources/archetype-resources/common/pom.xml
----------------------------------------------------------------------
diff --git a/archetype/src/main/resources/archetype-resources/common/pom.xml b/archetype/src/main/resources/archetype-resources/common/pom.xml
new file mode 100644
index 0000000..78862b9
--- /dev/null
+++ b/archetype/src/main/resources/archetype-resources/common/pom.xml
@@ -0,0 +1,60 @@
+<?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>${groupId}</groupId>
+    <artifactId>${rootArtifactId}</artifactId>
+    <version>${version}</version>
+  </parent>
+
+  <name>Apache Syncope sample project - Common</name>
+  <groupId>${groupId}</groupId>
+  <artifactId>${artifactId}</artifactId>
+  <packaging>jar</packaging>
+
+  <dependencies>
+
+    <dependency>
+      <groupId>org.apache.syncope.common</groupId>
+      <artifactId>syncope-common-lib</artifactId>
+    </dependency>
+
+  </dependencies>
+  
+  <build>    
+    <resources>
+      <resource>
+        <directory>src/main/resources</directory>
+        <filtering>true</filtering>
+      </resource>
+    </resources>
+
+    <testResources>
+      <testResource>
+        <directory>src/test/resources</directory>
+        <filtering>true</filtering>
+      </testResource>
+    </testResources>
+  </build>
+
+</project>

http://git-wip-us.apache.org/repos/asf/syncope/blob/213fbc60/archetype/src/main/resources/archetype-resources/console/pom.xml
----------------------------------------------------------------------
diff --git a/archetype/src/main/resources/archetype-resources/console/pom.xml b/archetype/src/main/resources/archetype-resources/console/pom.xml
index 77f873d..2a4ad8f 100644
--- a/archetype/src/main/resources/archetype-resources/console/pom.xml
+++ b/archetype/src/main/resources/archetype-resources/console/pom.xml
@@ -46,7 +46,13 @@ under the License.
       <groupId>javax.servlet</groupId>
       <artifactId>jstl</artifactId>
     </dependency>
-  
+
+    <dependency>
+      <groupId>${groupId}</groupId>
+      <artifactId>common</artifactId>
+      <version>${project.version}</version>
+    </dependency>  
+    
     <dependency>
       <groupId>org.apache.syncope.client</groupId>
       <artifactId>syncope-client-console</artifactId>
@@ -192,6 +198,19 @@ ORYX.Editor.createByUrl = function(modelUrl){"/>
       
       <dependencies>
         <dependency>
+          <groupId>org.apache.syncope.fit</groupId>
+          <artifactId>syncope-fit-build-tools</artifactId>
+          <version>${syncope.version}</version>
+          <type>war</type>
+          <scope>test</scope>
+        </dependency>
+        <dependency>
+          <groupId>net.tirasa.connid.bundles.soap</groupId>
+          <artifactId>wssample</artifactId>
+          <type>war</type>
+          <scope>test</scope>
+        </dependency>
+        <dependency>
           <groupId>com.h2database</groupId>
           <artifactId>h2</artifactId>
           <scope>test</scope>
@@ -212,7 +231,10 @@ ORYX.Editor.createByUrl = function(modelUrl){"/>
                 <phase>package</phase>
                 <configuration>
                   <target>
-                    <copy file="../core/target/test-classes/persistence.properties" 
+                    <copy todir="../core/target/syncope/WEB-INF/classes">
+                      <fileset dir="../core/target/test-classes/domains"/>
+                    </copy>
+                    <copy file="../core/target/test-classes/connid.properties" 
                           todir="../core/target/syncope/WEB-INF/classes" 
                           overwrite="true"/>
                     <copy file="../core/target/test-classes/db.jsp" 
@@ -256,6 +278,22 @@ ORYX.Editor.createByUrl = function(modelUrl){"/>
               </configuration>
               <deployables>
                 <deployable>
+                  <groupId>net.tirasa.connid.bundles.soap</groupId>
+                  <artifactId>wssample</artifactId>
+                  <type>war</type>
+                  <properties>
+                    <context>wssample</context>
+                  </properties>
+                </deployable>
+                <deployable>
+                  <groupId>org.apache.syncope.fit</groupId>
+                  <artifactId>syncope-fit-build-tools</artifactId>
+                  <type>war</type>
+                  <properties>
+                    <context>syncope-fit-build-tools</context>
+                  </properties>
+                </deployable>
+                <deployable>
                   <location>../core/target/syncope</location>
                   <properties>
                     <context>syncope</context>

http://git-wip-us.apache.org/repos/asf/syncope/blob/213fbc60/archetype/src/main/resources/archetype-resources/core/pom.xml
----------------------------------------------------------------------
diff --git a/archetype/src/main/resources/archetype-resources/core/pom.xml b/archetype/src/main/resources/archetype-resources/core/pom.xml
index 0dac78f..d45c223 100644
--- a/archetype/src/main/resources/archetype-resources/core/pom.xml
+++ b/archetype/src/main/resources/archetype-resources/core/pom.xml
@@ -47,6 +47,12 @@ under the License.
     </dependency>
     
     <dependency>
+      <groupId>${groupId}</groupId>
+      <artifactId>common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    
+    <dependency>
       <groupId>org.apache.syncope.core</groupId>
       <artifactId>syncope-core-rest-cxf</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/syncope/blob/213fbc60/archetype/src/main/resources/archetype-resources/pom.xml
----------------------------------------------------------------------
diff --git a/archetype/src/main/resources/archetype-resources/pom.xml b/archetype/src/main/resources/archetype-resources/pom.xml
index 6b2029c..0559bf0 100644
--- a/archetype/src/main/resources/archetype-resources/pom.xml
+++ b/archetype/src/main/resources/archetype-resources/pom.xml
@@ -39,6 +39,12 @@ under the License.
 
   <dependencyManagement>
     <dependencies>
+      <dependency>  
+        <groupId>org.apache.syncope.common</groupId>
+        <artifactId>syncope-common-lib</artifactId>
+        <version>${syncope.version}</version>
+      </dependency>
+      
       <dependency>
         <groupId>org.apache.syncope.core</groupId>
         <artifactId>syncope-core-rest-cxf</artifactId>
@@ -71,7 +77,7 @@ under the License.
     
   <build>
     
-    <!-- Disable legal check for generated projects: see SYNCOPE-84 -->
+    <!-- Disable checks for generated projects -->
     <plugins>
       <plugin>
         <groupId>org.codehaus.mojo</groupId>
@@ -99,10 +105,28 @@ under the License.
           </execution>
         </executions>
       </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+        <inherited>true</inherited>
+        <configuration>
+          <configLocation>sun_checks.xml</configLocation>
+          <targetJdk>${targetJdk}</targetJdk>
+        </configuration>
+        <executions>
+          <execution>
+            <goals>
+              <goal>check</goal>
+            </goals>
+            <phase>none</phase>
+          </execution>
+        </executions>
+      </plugin>
     </plugins>
   </build>
     
   <modules>
+    <module>common</module>
     <module>core</module>
     <module>console</module>
   </modules>

http://git-wip-us.apache.org/repos/asf/syncope/blob/213fbc60/archetype/src/main/resources/meta-pom.xml
----------------------------------------------------------------------
diff --git a/archetype/src/main/resources/meta-pom.xml b/archetype/src/main/resources/meta-pom.xml
index 4ce00b3..4145d2a 100644
--- a/archetype/src/main/resources/meta-pom.xml
+++ b/archetype/src/main/resources/meta-pom.xml
@@ -43,6 +43,12 @@ under the License.
 
   <dependencyManagement>
     <dependencies>
+      <dependency>  
+        <groupId>org.apache.syncope.common</groupId>
+        <artifactId>syncope-common-lib</artifactId>
+        <version>${syncope.version}</version>
+      </dependency>
+      
       <dependency>
         <groupId>org.apache.syncope.core</groupId>
         <artifactId>syncope-core-rest-cxf</artifactId>
@@ -75,7 +81,7 @@ under the License.
     
   <build>
     
-    <!-- Disable legal check for generated projects: see SYNCOPE-84 -->
+    <!-- Disable checks for generated projects -->
     <plugins>
       <plugin>
         <groupId>org.codehaus.mojo</groupId>
@@ -103,10 +109,28 @@ under the License.
           </execution>
         </executions>
       </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+        <inherited>true</inherited>
+        <configuration>
+          <configLocation>sun_checks.xml</configLocation>
+          <targetJdk>${targetJdk}</targetJdk>
+        </configuration>
+        <executions>
+          <execution>
+            <goals>
+              <goal>check</goal>
+            </goals>
+            <phase>none</phase>
+          </execution>
+        </executions>
+      </plugin>
     </plugins>
   </build>
     
   <modules>
+    <module>common</module>
     <module>core</module>
     <module>console</module>
   </modules>

http://git-wip-us.apache.org/repos/asf/syncope/blob/213fbc60/client/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorRestClient.java
----------------------------------------------------------------------
diff --git a/client/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorRestClient.java b/client/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorRestClient.java
index 8dab310..652e443 100644
--- a/client/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorRestClient.java
+++ b/client/console/src/main/java/org/apache/syncope/client/console/rest/ConnectorRestClient.java
@@ -66,14 +66,15 @@ public class ConnectorRestClient extends BaseRestClient {
     /**
      * Load an already existent connector by its name.
      *
-     * @param connectorInstanceId the id
+     * @param key the id
      * @return ConnInstanceTO
      */
-    public ConnInstanceTO read(final Long connectorInstanceId) {
+    public ConnInstanceTO read(final Long key) {
         ConnInstanceTO connectorTO = null;
 
         try {
-            connectorTO = getService(ConnectorService.class).read(connectorInstanceId);
+            connectorTO = getService(ConnectorService.class).
+                    read(key, SyncopeConsoleSession.get().getLocale().toString());
         } catch (SyncopeClientException e) {
             LOG.error("While reading a connector", e);
         }
@@ -88,10 +89,11 @@ public class ConnectorRestClient extends BaseRestClient {
         getService(ConnectorService.class).update(connectorTO);
     }
 
-    public ConnInstanceTO delete(final Long id) {
-        ConnInstanceTO instanceTO = getService(ConnectorService.class).read(id);
-        getService(ConnectorService.class).delete(id);
-        return instanceTO;
+    public ConnInstanceTO delete(final Long key) {
+        ConnInstanceTO connectorTO = getService(ConnectorService.class).
+                read(key, SyncopeConsoleSession.get().getLocale().toString());
+        getService(ConnectorService.class).delete(key);
+        return connectorTO;
     }
 
     public List<ConnBundleTO> getAllBundles() {

http://git-wip-us.apache.org/repos/asf/syncope/blob/213fbc60/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ConnectorService.java
----------------------------------------------------------------------
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ConnectorService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ConnectorService.java
index 2518a74..66da67c 100644
--- a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ConnectorService.java
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/ConnectorService.java
@@ -108,23 +108,28 @@ public interface ConnectorService extends JAXRSService {
      * Returns connector instance with matching key.
      *
      * @param key connector instance key to be read
+     * @param lang language to select property keys, null for default (English).
+     * An ISO 639 alpha-2 or alpha-3 language code, or a language subtag up to 8 characters in length.
      * @return connector instance with matching key
      */
     @GET
     @Path("{key}")
     @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    ConnInstanceTO read(@NotNull @PathParam("key") Long key);
+    ConnInstanceTO read(@NotNull @PathParam("key") Long key, @QueryParam("lang") String lang);
 
     /**
      * Returns connector instance for matching resource.
      *
      * @param resourceName resource name to be used for connector lookup
+     * @param lang language to select property keys, null for default (English).
+     * An ISO 639 alpha-2 or alpha-3 language code, or a language subtag up to 8 characters in length.
      * @return connector instance for matching resource
      */
     @GET
     @Path("byResource/{resourceName}")
     @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
-    ConnInstanceTO readByResource(@NotNull @PathParam("resourceName") String resourceName);
+    ConnInstanceTO readByResource(
+            @NotNull @PathParam("resourceName") String resourceName, @QueryParam("lang") String lang);
 
     /**
      * Returns a list of all connector instances with property keys in the matching language.

http://git-wip-us.apache.org/repos/asf/syncope/blob/213fbc60/core/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java
index 793686f..3319579 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/ConnectorLogic.java
@@ -26,7 +26,6 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 import org.apache.commons.collections4.CollectionUtils;
-import org.apache.commons.collections4.IteratorUtils;
 import org.apache.commons.collections4.PredicateUtils;
 import org.apache.commons.collections4.Transformer;
 import org.apache.commons.lang3.ArrayUtils;
@@ -108,10 +107,10 @@ public class ConnectorLogic extends AbstractTransactionalLogic<ConnInstanceTO> {
     }
 
     @PreAuthorize("hasRole('" + Entitlement.CONNECTOR_DELETE + "')")
-    public ConnInstanceTO delete(final Long connInstanceId) {
-        ConnInstance connInstance = connInstanceDAO.find(connInstanceId);
+    public ConnInstanceTO delete(final Long connInstanceKey) {
+        ConnInstance connInstance = connInstanceDAO.find(connInstanceKey);
         if (connInstance == null) {
-            throw new NotFoundException("Connector '" + connInstanceId + "'");
+            throw new NotFoundException("Connector '" + connInstanceKey + "'");
         }
 
         if (!connInstance.getResources().isEmpty()) {
@@ -125,7 +124,7 @@ public class ConnectorLogic extends AbstractTransactionalLogic<ConnInstanceTO> {
 
         ConnInstanceTO connToDelete = binder.getConnInstanceTO(connInstance);
 
-        connInstanceDAO.delete(connInstanceId);
+        connInstanceDAO.delete(connInstanceKey);
 
         return connToDelete;
     }
@@ -133,35 +132,36 @@ public class ConnectorLogic extends AbstractTransactionalLogic<ConnInstanceTO> {
     @PreAuthorize("hasRole('" + Entitlement.CONNECTOR_LIST + "')")
     @Transactional(readOnly = true)
     public List<ConnInstanceTO> list(final String lang) {
-        if (StringUtils.isBlank(lang)) {
-            CurrentLocale.set(Locale.ENGLISH);
-        } else {
-            CurrentLocale.set(new Locale(lang));
-        }
+        CurrentLocale.set(StringUtils.isBlank(lang) ? Locale.ENGLISH : new Locale(lang));
 
-        return CollectionUtils.collect(IteratorUtils.filteredIterator(connInstanceDAO.findAll().iterator(),
-                PredicateUtils.notNullPredicate()), new Transformer<ConnInstance, ConnInstanceTO>() {
+        List<ConnInstanceTO> result = CollectionUtils.collect(connInstanceDAO.findAll().iterator(),
+                new Transformer<ConnInstance, ConnInstanceTO>() {
 
                     @Override
-                    public ConnInstanceTO transform(final ConnInstance input) {
+                    public ConnInstanceTO transform(final ConnInstance connInstance) {
                         ConnInstanceTO result = null;
                         try {
-                            result = binder.getConnInstanceTO(input);
+                            result = binder.getConnInstanceTO(connInstance);
                         } catch (NotFoundException e) {
-                            LOG.error("Connector '{}#{}' not found", input.getBundleName(), input.getVersion());
+                            LOG.error("Connector '{}#{}' not found",
+                                    connInstance.getBundleName(), connInstance.getVersion());
                         }
 
                         return result;
                     }
                 }, new ArrayList<ConnInstanceTO>());
+        CollectionUtils.filter(result, PredicateUtils.notNullPredicate());
+        return result;
     }
 
     @PreAuthorize("hasRole('" + Entitlement.CONNECTOR_READ + "')")
     @Transactional(readOnly = true)
-    public ConnInstanceTO read(final Long connInstanceId) {
-        ConnInstance connInstance = connInstanceDAO.find(connInstanceId);
+    public ConnInstanceTO read(final Long connInstanceKey, final String lang) {
+        CurrentLocale.set(StringUtils.isBlank(lang) ? Locale.ENGLISH : new Locale(lang));
+
+        ConnInstance connInstance = connInstanceDAO.find(connInstanceKey);
         if (connInstance == null) {
-            throw new NotFoundException("Connector '" + connInstanceId + "'");
+            throw new NotFoundException("Connector '" + connInstanceKey + "'");
         }
 
         return binder.getConnInstanceTO(connInstance);
@@ -229,8 +229,8 @@ public class ConnectorLogic extends AbstractTransactionalLogic<ConnInstanceTO> {
         }
 
         // consider the possibility to receive overridden properties only
-        final Set<ConnConfProperty> conf = binder.mergeConnConfProperties(connInstanceTO.getConfiguration(),
-                connInstance.getConfiguration());
+        Set<ConnConfProperty> conf =
+                binder.mergeConnConfProperties(connInstanceTO.getConfiguration(), connInstance.getConfiguration());
 
         // We cannot use Spring bean because this method could be used during resource definition or modification:
         // bean couldn't exist or couldn't be updated.
@@ -247,14 +247,14 @@ public class ConnectorLogic extends AbstractTransactionalLogic<ConnInstanceTO> {
 
     @PreAuthorize("hasRole('" + Entitlement.CONNECTOR_READ + "')")
     @Transactional(readOnly = true)
-    public List<ConnConfProperty> getConfigurationProperties(final Long connInstanceId) {
+    public List<ConnConfProperty> getConfigurationProperties(final Long connInstanceKey) {
 
-        ConnInstance connInstance = connInstanceDAO.find(connInstanceId);
+        ConnInstance connInstance = connInstanceDAO.find(connInstanceKey);
         if (connInstance == null) {
-            throw new NotFoundException("Connector '" + connInstanceId + "'");
+            throw new NotFoundException("Connector '" + connInstanceKey + "'");
         }
 
-        return new ArrayList<ConnConfProperty>(connInstance.getConfiguration());
+        return new ArrayList<>(connInstance.getConfiguration());
     }
 
     @PreAuthorize("hasRole('" + Entitlement.CONNECTOR_READ + "')")
@@ -277,7 +277,9 @@ public class ConnectorLogic extends AbstractTransactionalLogic<ConnInstanceTO> {
 
     @PreAuthorize("hasRole('" + Entitlement.CONNECTOR_READ + "')")
     @Transactional(readOnly = true)
-    public ConnInstanceTO readByResource(final String resourceName) {
+    public ConnInstanceTO readByResource(final String resourceName, final String lang) {
+        CurrentLocale.set(StringUtils.isBlank(lang) ? Locale.ENGLISH : new Locale(lang));
+
         ExternalResource resource = resourceDAO.find(resourceName);
         if (resource == null) {
             throw new NotFoundException("Resource '" + resourceName + "'");
@@ -296,21 +298,21 @@ public class ConnectorLogic extends AbstractTransactionalLogic<ConnInstanceTO> {
     protected ConnInstanceTO resolveReference(final Method method, final Object... args)
             throws UnresolvedReferenceException {
 
-        Long id = null;
+        Long key = null;
 
         if (ArrayUtils.isNotEmpty(args)) {
-            for (int i = 0; id == null && i < args.length; i++) {
+            for (int i = 0; key == null && i < args.length; i++) {
                 if (args[i] instanceof Long) {
-                    id = (Long) args[i];
+                    key = (Long) args[i];
                 } else if (args[i] instanceof ConnInstanceTO) {
-                    id = ((ConnInstanceTO) args[i]).getKey();
+                    key = ((ConnInstanceTO) args[i]).getKey();
                 }
             }
         }
 
-        if ((id != null) && !id.equals(0L)) {
+        if ((key != null) && !key.equals(0L)) {
             try {
-                return binder.getConnInstanceTO(connInstanceDAO.find(id));
+                return binder.getConnInstanceTO(connInstanceDAO.find(key));
             } catch (Throwable ignore) {
                 LOG.debug("Unresolved reference", ignore);
                 throw new UnresolvedReferenceException(ignore);

http://git-wip-us.apache.org/repos/asf/syncope/blob/213fbc60/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index 6f912b6..50e7b06 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -558,7 +558,7 @@ under the License.
                 location="${connid.location}"
                 connectorName="net.tirasa.connid.bundles.ldap.LdapConnector"
                 version="${connid.ldap.version}" 
-                jsonConf='[{"schema":{"name":"synchronizePasswords","displayName":"Enable Password Synchronization","helpMessage":"If true, the connector will synchronize passwords. The Password Capture Plugin needs to be installed for password synchronization to work.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"maintainLdapGroupMembership","displayName":"Maintain LDAP Group Membership","helpMessage":"When enabled and a user is renamed or deleted, update any LDAP groups to which the user belongs to reflect the new name. Otherwise, the LDAP resource must maintain referential integrity with respect to group membership.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["true"]},{"schema":{"name":"host","displayName":"Host","helpMessage":"The name or IP address of the host where the LDAP server is running.","type":"jav
 a.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["localhost"]},{"schema":{"name":"passwordHashAlgorithm","displayName":"Password Hash Algorithm","helpMessage":"Indicates the algorithm that the Identity system should use to hash the password. Currently supported values are SSHA, SHA, SSHA1, and SHA1. A blank value indicates that the system will not hash passwords. This will cause cleartext passwords to be stored in LDAP unless the LDAP server performs the hash (Netscape Directory Server and iPlanet Directory Server do).","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["SHA"]},{"schema":{"name":"blockSize","displayName":"Block Size","helpMessage":"The maximum number of accounts that can be in a block when retrieving accounts in blocks.","type":"int","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"valu
 es":[]},{"schema":{"name":"useBlocks","displayName":"Use Blocks","helpMessage":"When performing operations on large numbers of accounts, the accounts are processed in blocks to reduce the amount of memory used by the operation. Select this option to process accounts in blocks.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[false]},{"schema":{"name":"usePagedResultControl","displayName":"Use Paged Result Control","helpMessage":"When enabled, the LDAP Paged Results control is preferred over the VLV control when retrieving accounts.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"port","displayName":"TCP Port","helpMessage":"TCP/IP port number used to communicate with the LDAP server.","type":"int","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[1389]},{"schema":{"nam
 e":"vlvSortAttribute","displayName":"VLV Sort Attribute","helpMessage":"Specify the sort attribute to use for VLV indexes on the resource.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"statusManagementClass","displayName":"Status management class ","helpMessage":"Class to be used to manage enabled/disabled status. If no class is specified then identity status management wont be possible.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["net.tirasa.connid.bundles.ldap.commons.AttributeStatusManagement"]},{"schema":{"name":"accountObjectClasses","displayName":"Account Object Classes","helpMessage":"The object class or classes that will be used when creating new user objects in the LDAP tree. When entering more than one object class, each entry should be on its own line; do not use commas or semi-colons to s
 eparate multiple object classes. Some object classes may require that you specify all object classes in the class hierarchy.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["inetOrgPerson"]},{"schema":{"name":"accountUserNameAttributes","displayName":"Account User Name Attributes","helpMessage":"Attribute or attributes which holds the account user name. They will be used when authenticating to find the LDAP entry for the user name to authenticate.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["uid"]},{"schema":{"name":"baseContextsToSynchronize","displayName":"Base Contexts to Synchronize","helpMessage":"One or more starting points in the LDAP tree that will be used to determine if a change should be synchronized. The base contexts attribute will be used to synchronize a change if this property is not set.","type":"[Lja
 va.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["ou=people,o=isp","ou=groups,o=isp"]},{"schema":{"name":"accountSynchronizationFilter","displayName":"LDAP Filter for Accounts to Synchronize","helpMessage":"An optional LDAP filter for the objects to synchronize. Because the change log is for all objects, this filter updates only objects that match the specified filter. If you specify a filter, an object will be synchronized only if it matches the filter and includes a synchronized object class.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"removeLogEntryObjectClassFromFilter","displayName":"Remove Log Entry Object Class from Filter","helpMessage":"If this property is set (the default), the filter used to fetch change log entries does not contain the \"changeLogEntry\" object class, expecting that there are no entri
 es of other object types in the change log.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"passwordDecryptionKey","displayName":"Password Decryption Key","helpMessage":"The key to decrypt passwords with when performing password synchronization.","type":"org.identityconnectors.common.security.GuardedByteArray","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"readSchema","displayName":"Read Schema","helpMessage":"If true, the connector will read the schema from the server. If false, the connector will provide a default schema based on the object classes in the configuration. This property must be true in order to use extended object classes.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"ssl","displayName":"SSL","hel
 pMessage":"Select the check box to connect to the LDAP server using SSL.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"passwordAttributeToSynchronize","displayName":"Password Attribute to Synchronize","helpMessage":"The name of the password attribute to synchronize when performing password synchronization.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"accountSearchFilter","displayName":"LDAP Filter for Retrieving Accounts","helpMessage":"An optional LDAP filter to control which accounts are returned from the LDAP resource. If no filter is specified, only accounts that include all specified object classes are returned.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["uid=*"]},{"schema":{"name":"passwordDec
 ryptionInitializationVector","displayName":"Password Decryption Initialization Vector","helpMessage":"The initialization vector to decrypt passwords with when performing password synchronization.","type":"org.identityconnectors.common.security.GuardedByteArray","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"groupMemberAttribute","displayName":"Group Member Attribute","helpMessage":"The name of the group attribute that will be updated with the distinguished name of the user when the user is added to the group.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"failover","displayName":"Failover Servers","helpMessage":"List all servers that should be used for failover in case the preferred server fails. If the preferred server fails, JNDI will connect to the next available server in the list. List all servers in the
  form of \"ldap://ldap.example.com:389/\", which follows the standard LDAP v3 URLs described in RFC 2255. Only the host and port parts of the URL are relevant in this setting.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"modifiersNamesToFilterOut","displayName":"Filter Out Changes By","helpMessage":"The names (DNs) of directory administrators to filter from the changes. Changes with the attribute \"modifiersName\" that match entries in this list will be filtered out. The standard value is the administrator name used by this adapter, to prevent loops. Entries should be of the format \"cn=Directory Manager\".","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"groupNameAttributes","displayName":"Group Name Attributes","helpMessage":"Attribute or attributes which holds the group nam
 e.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["cn"]},{"schema":{"name":"uidAttribute","displayName":"Uid Attribute","helpMessage":"The name of the LDAP attribute which is mapped to the Uid attribute.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["cn"]},{"schema":{"name":"respectResourcePasswordPolicyChangeAfterReset","displayName":"Respect Resource Password Policy Change-After-Reset","helpMessage":"When this resource is specified in a Login Module (i.e., this resource is a pass-through authentication target) and the resource password policy is configured for change-after-reset, a user whose resource account password has been administratively reset will be required to change that password after successfully authenticating.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overrida
 ble":false,"values":["false"]},{"schema":{"name":"filterWithOrInsteadOfAnd","displayName":"Filter with Or Instead of And","helpMessage":"Normally the the filter used to fetch change log entries is an and-based filter retrieving an interval of change entries. If this property is set, the filter will or together the required change numbers instead.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"principal","displayName":"Principal","helpMessage":"The distinguished name with which to authenticate to the LDAP server.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["uid=admin,ou=system"]},{"schema":{"name":"changeLogBlockSize","displayName":"Change Log Block Size","helpMessage":"The number of change log entries to fetch per query.","type":"int","required":true,"order":0,"confidential":false,"defaultValues":null},
 "overridable":false,"values":[100]},{"schema":{"name":"baseContexts","displayName":"Base Contexts","helpMessage":"One or more starting points in the LDAP tree that will be used when searching the tree. Searches are performed when discovering users from the LDAP server or when looking for the groups of which a user is a member.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["ou=people,o=isp","ou=groups,o=isp"]},{"schema":{"name":"passwordAttribute","displayName":"Password Attribute","helpMessage":"The name of the LDAP attribute which holds the password. When changing an user password, the new password is set to this attribute.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["userpassword"]},{"schema":{"name":"changeNumberAttribute","displayName":"Change Number Attribute","helpMessage":"The name of the change number attribute
  in the change log entry.","type":"java.lang.String","required":true,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["changeNumber"]},{"schema":{"name":"objectClassesToSynchronize","displayName":"Object Classes to Synchronize","helpMessage":"The object classes to synchronize. The change log is for all objects; this filters updates to just the listed object classes. You should not list the superclasses of an object class unless you intend to synchronize objects with any of the superclass values. For example, if only \"inetOrgPerson\" objects should be synchronized, but the superclasses of \"inetOrgPerson\" (\"person\", \"organizationalperson\" and \"top\") should be filtered out, then list only \"inetOrgPerson\" here. All objects in LDAP are subclassed from \"top\". For this reason, you should never list \"top\", otherwise no object would be filtered.","type":"[Ljava.lang.String;","required":true,"order":0,"confidential":false,"defaultValues":null},
 "overridable":false,"values":["inetOrgPerson","groupOfUniqueNames"]},{"schema":{"name":"credentials","displayName":"Password","helpMessage":"Password for the principal.","type":"org.identityconnectors.common.security.GuardedString","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["secret"]},{"schema":{"name":"attributesToSynchronize","displayName":"Attributes to Synchronize","helpMessage":"The names of the attributes to synchronize. This ignores updates from the change log if they do not update any of the named attributes. For example, if only \"department\" is listed, then only changes that affect \"department\" will be processed. All other updates are ignored. If blank (the default), then all changes are processed.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"maintainPosixGroupMembership","displayName":"Maintain POSIX Group M
 embership","helpMessage":"When enabled and a user is renamed or deleted, update any POSIX groups to which the user belongs to reflect the new name. Otherwise, the LDAP resource must maintain referential integrity with respect to group membership.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["truemaintainLdapGroupMembership"]}]'/>
+                jsonConf='[{"schema":{"name":"synchronizePasswords","displayName":"Enable Password Synchronization","helpMessage":"If true, the connector will synchronize passwords. The Password Capture Plugin needs to be installed for password synchronization to work.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"maintainLdapGroupMembership","displayName":"Maintain LDAP Group Membership","helpMessage":"When enabled and a user is renamed or deleted, update any LDAP groups to which the user belongs to reflect the new name. Otherwise, the LDAP resource must maintain referential integrity with respect to group membership.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["true"]},{"schema":{"name":"host","displayName":"Host","helpMessage":"The name or IP address of the host where the LDAP server is running.","type":"jav
 a.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["localhost"]},{"schema":{"name":"passwordHashAlgorithm","displayName":"Password Hash Algorithm","helpMessage":"Indicates the algorithm that the Identity system should use to hash the password. Currently supported values are SSHA, SHA, SSHA1, and SHA1. A blank value indicates that the system will not hash passwords. This will cause cleartext passwords to be stored in LDAP unless the LDAP server performs the hash (Netscape Directory Server and iPlanet Directory Server do).","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["SHA"]},{"schema":{"name":"blockSize","displayName":"Block Size","helpMessage":"The maximum number of accounts that can be in a block when retrieving accounts in blocks.","type":"int","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"valu
 es":[]},{"schema":{"name":"useBlocks","displayName":"Use Blocks","helpMessage":"When performing operations on large numbers of accounts, the accounts are processed in blocks to reduce the amount of memory used by the operation. Select this option to process accounts in blocks.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[false]},{"schema":{"name":"usePagedResultControl","displayName":"Use Paged Result Control","helpMessage":"When enabled, the LDAP Paged Results control is preferred over the VLV control when retrieving accounts.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"port","displayName":"TCP Port","helpMessage":"TCP/IP port number used to communicate with the LDAP server.","type":"int","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[1389]},{"schema":{"nam
 e":"vlvSortAttribute","displayName":"VLV Sort Attribute","helpMessage":"Specify the sort attribute to use for VLV indexes on the resource.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"statusManagementClass","displayName":"Status management class ","helpMessage":"Class to be used to manage enabled/disabled status. If no class is specified then identity status management wont be possible.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["net.tirasa.connid.bundles.ldap.commons.AttributeStatusManagement"]},{"schema":{"name":"accountObjectClasses","displayName":"Account Object Classes","helpMessage":"The object class or classes that will be used when creating new user objects in the LDAP tree. When entering more than one object class, each entry should be on its own line; do not use commas or semi-colons to s
 eparate multiple object classes. Some object classes may require that you specify all object classes in the class hierarchy.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["inetOrgPerson"]},{"schema":{"name":"accountUserNameAttributes","displayName":"Account User Name Attributes","helpMessage":"Attribute or attributes which holds the account user name. They will be used when authenticating to find the LDAP entry for the user name to authenticate.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["uid"]},{"schema":{"name":"baseContextsToSynchronize","displayName":"Base Contexts to Synchronize","helpMessage":"One or more starting points in the LDAP tree that will be used to determine if a change should be synchronized. The base contexts attribute will be used to synchronize a change if this property is not set.","type":"[Lja
 va.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["ou=people,o=isp","ou=groups,o=isp"]},{"schema":{"name":"accountSynchronizationFilter","displayName":"LDAP Filter for Accounts to Synchronize","helpMessage":"An optional LDAP filter for the objects to synchronize. Because the change log is for all objects, this filter updates only objects that match the specified filter. If you specify a filter, an object will be synchronized only if it matches the filter and includes a synchronized object class.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"removeLogEntryObjectClassFromFilter","displayName":"Remove Log Entry Object Class from Filter","helpMessage":"If this property is set (the default), the filter used to fetch change log entries does not contain the \"changeLogEntry\" object class, expecting that there are no entri
 es of other object types in the change log.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"passwordDecryptionKey","displayName":"Password Decryption Key","helpMessage":"The key to decrypt passwords with when performing password synchronization.","type":"org.identityconnectors.common.security.GuardedByteArray","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"readSchema","displayName":"Read Schema","helpMessage":"If true, the connector will read the schema from the server. If false, the connector will provide a default schema based on the object classes in the configuration. This property must be true in order to use extended object classes.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"ssl","displayName":"SSL","hel
 pMessage":"Select the check box to connect to the LDAP server using SSL.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"passwordAttributeToSynchronize","displayName":"Password Attribute to Synchronize","helpMessage":"The name of the password attribute to synchronize when performing password synchronization.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"accountSearchFilter","displayName":"LDAP Filter for Retrieving Accounts","helpMessage":"An optional LDAP filter to control which accounts are returned from the LDAP resource. If no filter is specified, only accounts that include all specified object classes are returned.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["uid=*"]},{"schema":{"name":"passwordDec
 ryptionInitializationVector","displayName":"Password Decryption Initialization Vector","helpMessage":"The initialization vector to decrypt passwords with when performing password synchronization.","type":"org.identityconnectors.common.security.GuardedByteArray","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"groupMemberAttribute","displayName":"Group Member Attribute","helpMessage":"The name of the group attribute that will be updated with the distinguished name of the user when the user is added to the group.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"failover","displayName":"Failover Servers","helpMessage":"List all servers that should be used for failover in case the preferred server fails. If the preferred server fails, JNDI will connect to the next available server in the list. List all servers in the
  form of \"ldap://ldap.example.com:389/\", which follows the standard LDAP v3 URLs described in RFC 2255. Only the host and port parts of the URL are relevant in this setting.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"modifiersNamesToFilterOut","displayName":"Filter Out Changes By","helpMessage":"The names (DNs) of directory administrators to filter from the changes. Changes with the attribute \"modifiersName\" that match entries in this list will be filtered out. The standard value is the administrator name used by this adapter, to prevent loops. Entries should be of the format \"cn=Directory Manager\".","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"groupNameAttributes","displayName":"Group Name Attributes","helpMessage":"Attribute or attributes which holds the group nam
 e.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["cn"]},{"schema":{"name":"uidAttribute","displayName":"Uid Attribute","helpMessage":"The name of the LDAP attribute which is mapped to the Uid attribute.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["cn"]},{"schema":{"name":"respectResourcePasswordPolicyChangeAfterReset","displayName":"Respect Resource Password Policy Change-After-Reset","helpMessage":"When this resource is specified in a Login Module (i.e., this resource is a pass-through authentication target) and the resource password policy is configured for change-after-reset, a user whose resource account password has been administratively reset will be required to change that password after successfully authenticating.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overrida
 ble":false,"values":["false"]},{"schema":{"name":"filterWithOrInsteadOfAnd","displayName":"Filter with Or Instead of And","helpMessage":"Normally the the filter used to fetch change log entries is an and-based filter retrieving an interval of change entries. If this property is set, the filter will or together the required change numbers instead.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]},{"schema":{"name":"principal","displayName":"Principal","helpMessage":"The distinguished name with which to authenticate to the LDAP server.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["uid=admin,ou=system"]},{"schema":{"name":"changeLogBlockSize","displayName":"Change Log Block Size","helpMessage":"The number of change log entries to fetch per query.","type":"int","required":true,"order":0,"confidential":false,"defaultValues":null},
 "overridable":false,"values":[100]},{"schema":{"name":"baseContexts","displayName":"Base Contexts","helpMessage":"One or more starting points in the LDAP tree that will be used when searching the tree. Searches are performed when discovering users from the LDAP server or when looking for the groups of which a user is a member.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["ou=people,o=isp","ou=groups,o=isp"]},{"schema":{"name":"passwordAttribute","displayName":"Password Attribute","helpMessage":"The name of the LDAP attribute which holds the password. When changing an user password, the new password is set to this attribute.","type":"java.lang.String","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["userpassword"]},{"schema":{"name":"changeNumberAttribute","displayName":"Change Number Attribute","helpMessage":"The name of the change number attribute
  in the change log entry.","type":"java.lang.String","required":true,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["changeNumber"]},{"schema":{"name":"objectClassesToSynchronize","displayName":"Object Classes to Synchronize","helpMessage":"The object classes to synchronize. The change log is for all objects; this filters updates to just the listed object classes. You should not list the superclasses of an object class unless you intend to synchronize objects with any of the superclass values. For example, if only \"inetOrgPerson\" objects should be synchronized, but the superclasses of \"inetOrgPerson\" (\"person\", \"organizationalperson\" and \"top\") should be filtered out, then list only \"inetOrgPerson\" here. All objects in LDAP are subclassed from \"top\". For this reason, you should never list \"top\", otherwise no object would be filtered.","type":"[Ljava.lang.String;","required":true,"order":0,"confidential":false,"defaultValues":null},
 "overridable":false,"values":["inetOrgPerson","groupOfUniqueNames"]},{"schema":{"name":"credentials","displayName":"Password","helpMessage":"Password for the principal.","type":"org.identityconnectors.common.security.GuardedString","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["secret"]},{"schema":{"name":"attributesToSynchronize","displayName":"Attributes to Synchronize","helpMessage":"The names of the attributes to synchronize. This ignores updates from the change log if they do not update any of the named attributes. For example, if only \"department\" is listed, then only changes that affect \"department\" will be processed. All other updates are ignored. If blank (the default), then all changes are processed.","type":"[Ljava.lang.String;","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":[]},{"schema":{"name":"maintainPosixGroupMembership","displayName":"Maintain POSIX Group M
 embership","helpMessage":"When enabled and a user is renamed or deleted, update any POSIX groups to which the user belongs to reflect the new name. Otherwise, the LDAP resource must maintain referential integrity with respect to group membership.","type":"boolean","required":false,"order":0,"confidential":false,"defaultValues":null},"overridable":false,"values":["false"]}]'/>
   <ConnInstance_capabilities ConnInstance_id="105" capability="ONE_PHASE_CREATE"/>
   <ConnInstance_capabilities ConnInstance_id="105" capability="ONE_PHASE_UPDATE"/>
   <ConnInstance_capabilities ConnInstance_id="105" capability="ONE_PHASE_DELETE"/>

http://git-wip-us.apache.org/repos/asf/syncope/blob/213fbc60/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnIdBundleManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnIdBundleManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnIdBundleManagerImpl.java
index 268453a..866f41f 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnIdBundleManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ConnIdBundleManagerImpl.java
@@ -128,13 +128,13 @@ public class ConnIdBundleManagerImpl implements ConnIdBundleManager {
 
     private void initRemote(final URI location) {
         // 1. Extract conf params for remote connection from given URI
-        final String host = location.getHost();
-        final int port = location.getPort();
-        final GuardedString key = new GuardedString(location.getUserInfo().toCharArray());
-        final boolean useSSL = location.getScheme().equals("connids");
+        String host = location.getHost();
+        int port = location.getPort();
+        GuardedString key = new GuardedString(location.getUserInfo().toCharArray());
+        boolean useSSL = location.getScheme().equals("connids");
 
-        final List<TrustManager> trustManagers = new ArrayList<>();
-        final String[] params = StringUtils.isBlank(location.getQuery()) ? null : location.getQuery().split("&");
+        List<TrustManager> trustManagers = new ArrayList<>();
+        String[] params = StringUtils.isBlank(location.getQuery()) ? null : location.getQuery().split("&");
         if (params != null && params.length > 0) {
             final String[] trustAllCerts = params[0].split("=");
             if (trustAllCerts != null && trustAllCerts.length > 1

http://git-wip-us.apache.org/repos/asf/syncope/blob/213fbc60/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ConnectorServiceImpl.java
----------------------------------------------------------------------
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ConnectorServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ConnectorServiceImpl.java
index bc43165..8f7b503 100644
--- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ConnectorServiceImpl.java
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/ConnectorServiceImpl.java
@@ -102,13 +102,13 @@ public class ConnectorServiceImpl extends AbstractServiceImpl implements Connect
     }
 
     @Override
-    public ConnInstanceTO read(final Long key) {
-        return logic.read(key);
+    public ConnInstanceTO read(final Long key, final String lang) {
+        return logic.read(key, lang);
     }
 
     @Override
-    public ConnInstanceTO readByResource(final String resourceName) {
-        return logic.readByResource(resourceName);
+    public ConnInstanceTO readByResource(final String resourceName, final String lang) {
+        return logic.readByResource(resourceName, lang);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/syncope/blob/213fbc60/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/H2StartStopListener.java
----------------------------------------------------------------------
diff --git a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/H2StartStopListener.java b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/H2StartStopListener.java
index feee732..3352720 100644
--- a/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/H2StartStopListener.java
+++ b/fit/build-tools/src/main/java/org/apache/syncope/fit/buildtools/H2StartStopListener.java
@@ -36,9 +36,6 @@ import org.springframework.web.context.support.WebApplicationContextUtils;
  */
 public class H2StartStopListener implements ServletContextListener {
 
-    /**
-     * Logger.
-     */
     private static final Logger LOG = LoggerFactory.getLogger(H2StartStopListener.class);
 
     private static final String H2_TESTDB = "h2TestDb";

http://git-wip-us.apache.org/repos/asf/syncope/blob/213fbc60/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AbstractITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AbstractITCase.java
index 368035e..4e5d5e9 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AbstractITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/AbstractITCase.java
@@ -22,6 +22,7 @@ import static org.junit.Assert.assertNotNull;
 
 import java.io.InputStream;
 import java.net.URI;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Properties;
 import java.util.UUID;
@@ -403,7 +404,7 @@ public abstract class AbstractITCase {
             throws NamingException {
         ResourceTO ldapRes = resourceService.read(RESOURCE_NAME_LDAP);
         final Map<String, ConnConfProperty> ldapConnConf =
-                connectorService.read(ldapRes.getConnector()).getConfigurationMap();
+                connectorService.read(ldapRes.getConnector(), Locale.ENGLISH.getLanguage()).getConfigurationMap();
 
         Properties env = new Properties();
         env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");

http://git-wip-us.apache.org/repos/asf/syncope/blob/213fbc60/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/ConnectorITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/ConnectorITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/ConnectorITCase.java
index 44ebf33..52ee2ac 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/ConnectorITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/ConnectorITCase.java
@@ -32,6 +32,7 @@ import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
@@ -115,7 +116,7 @@ public class ConnectorITCase extends AbstractITCase {
     @Test
     public void create() {
         ConnInstanceTO connectorTO = new ConnInstanceTO();
-        connectorTO.setLocation(connectorService.read(100L).getLocation());
+        connectorTO.setLocation(connectorService.read(100L, Locale.ENGLISH.getLanguage()).getLocation());
         connectorTO.setVersion(connidSoapVersion);
         connectorTO.setConnectorName("net.tirasa.connid.bundles.soap.WebServiceConnector");
         connectorTO.setBundleName("net.tirasa.connid.bundles.soap");
@@ -183,7 +184,7 @@ public class ConnectorITCase extends AbstractITCase {
 
         try {
             connectorService.update(actual);
-            actual = connectorService.read(actual.getKey());
+            actual = connectorService.read(actual.getKey(), Locale.ENGLISH.getLanguage());
         } catch (SyncopeClientException e) {
             LOG.error("update failed", e);
             t = e;
@@ -207,7 +208,7 @@ public class ConnectorITCase extends AbstractITCase {
 
         // check the non existence
         try {
-            connectorService.read(actual.getKey());
+            connectorService.read(actual.getKey(), Locale.ENGLISH.getLanguage());
         } catch (SyncopeClientException e) {
             assertEquals(Response.Status.NOT_FOUND, e.getType().getResponseStatus());
         }
@@ -256,11 +257,11 @@ public class ConnectorITCase extends AbstractITCase {
         connectorTO.getConfiguration().addAll(conf);
 
         connectorService.update(connectorTO);
-        ConnInstanceTO actual = connectorService.read(connectorTO.getKey());
+        ConnInstanceTO actual = connectorService.read(connectorTO.getKey(), Locale.ENGLISH.getLanguage());
 
         assertNotNull(actual);
 
-        actual = connectorService.read(actual.getKey());
+        actual = connectorService.read(actual.getKey(), Locale.ENGLISH.getLanguage());
 
         assertNotNull(actual);
         assertEquals(actual.getBundleName(), connectorTO.getBundleName());
@@ -287,7 +288,7 @@ public class ConnectorITCase extends AbstractITCase {
         // Copy resource and connector in order to create new objects.
         // ----------------------------------
         // Retrieve a connector instance template.
-        ConnInstanceTO connInstanceTO = connectorService.read(103L);
+        ConnInstanceTO connInstanceTO = connectorService.read(103L, Locale.ENGLISH.getLanguage());
         assertNotNull(connInstanceTO);
 
         // check for resource
@@ -338,7 +339,8 @@ public class ConnectorITCase extends AbstractITCase {
         // ----------------------------------
         // Check for spring bean.
         // ----------------------------------
-        ConnInstanceTO connInstanceBean = connectorService.readByResource(resourceTO.getKey());
+        ConnInstanceTO connInstanceBean = connectorService.readByResource(
+                resourceTO.getKey(), Locale.ENGLISH.getLanguage());
         assertNotNull(connInstanceBean);
         assertFalse(connInstanceBean.getCapabilities().contains(ConnectorCapability.AUTHENTICATE));
         // ----------------------------------
@@ -349,12 +351,12 @@ public class ConnectorITCase extends AbstractITCase {
         connInstanceTO.getCapabilities().add(ConnectorCapability.AUTHENTICATE);
 
         connectorService.update(connInstanceTO);
-        ConnInstanceTO actual = connectorService.read(connInstanceTO.getKey());
+        ConnInstanceTO actual = connectorService.read(connInstanceTO.getKey(), Locale.ENGLISH.getLanguage());
         assertNotNull(actual);
         assertTrue(connInstanceTO.getCapabilities().contains(ConnectorCapability.AUTHENTICATE));
 
         // check for spring bean update
-        connInstanceBean = connectorService.readByResource(resourceTO.getKey());
+        connInstanceBean = connectorService.readByResource(resourceTO.getKey(), Locale.ENGLISH.getLanguage());
         assertTrue(connInstanceBean.getCapabilities().contains(ConnectorCapability.AUTHENTICATE));
         // ----------------------------------
     }
@@ -380,13 +382,13 @@ public class ConnectorITCase extends AbstractITCase {
 
     @Test
     public void read() {
-        ConnInstanceTO connectorInstanceTO = connectorService.read(100L);
+        ConnInstanceTO connectorInstanceTO = connectorService.read(100L, Locale.ENGLISH.getLanguage());
         assertNotNull(connectorInstanceTO);
     }
 
     @Test
     public void getBundles() {
-        List<ConnBundleTO> bundles = connectorService.getBundles(null);
+        List<ConnBundleTO> bundles = connectorService.getBundles(Locale.ENGLISH.getLanguage());
         assertNotNull(bundles);
         assertFalse(bundles.isEmpty());
         for (ConnBundleTO bundle : bundles) {
@@ -403,7 +405,7 @@ public class ConnectorITCase extends AbstractITCase {
 
     @Test
     public void checkHiddenProperty() {
-        ConnInstanceTO connInstanceTO = connectorService.read(100L);
+        ConnInstanceTO connInstanceTO = connectorService.read(100L, Locale.ENGLISH.getLanguage());
 
         boolean check = false;
 
@@ -529,7 +531,7 @@ public class ConnectorITCase extends AbstractITCase {
 
     @Test
     public void getSchemaNames() {
-        ConnInstanceTO conn = connectorService.read(101L);
+        ConnInstanceTO conn = connectorService.read(101L, Locale.ENGLISH.getLanguage());
 
         List<PlainSchemaTO> schemaNames = connectorService.getSchemaNames(conn.getKey(), conn, true);
         assertNotNull(schemaNames);
@@ -542,7 +544,7 @@ public class ConnectorITCase extends AbstractITCase {
         assertNotNull(schemaNames);
         assertEquals(1, schemaNames.size());
 
-        conn = connectorService.read(104L);
+        conn = connectorService.read(104L, Locale.ENGLISH.getLanguage());
 
         // to be used with overridden properties
         conn.getConfiguration().clear();
@@ -554,7 +556,7 @@ public class ConnectorITCase extends AbstractITCase {
 
     @Test
     public void getSupportedObjectClasses() {
-        ConnInstanceTO ldap = connectorService.read(105L);
+        ConnInstanceTO ldap = connectorService.read(105L, Locale.ENGLISH.getLanguage());
         assertNotNull(ldap);
 
         List<ConnIdObjectClass> objectClasses = connectorService.getSupportedObjectClasses(ldap.getKey(), ldap);
@@ -565,7 +567,7 @@ public class ConnectorITCase extends AbstractITCase {
         assertTrue(objectClasses.contains(
                 ConnIdObjectClass.getInstance(ConnIdObjectClass.class, ObjectClass.GROUP_NAME)));
 
-        ConnInstanceTO csv = connectorService.read(104L);
+        ConnInstanceTO csv = connectorService.read(104L, Locale.ENGLISH.getLanguage());
         assertNotNull(csv);
 
         objectClasses = connectorService.getSupportedObjectClasses(csv.getKey(), csv);
@@ -582,7 +584,7 @@ public class ConnectorITCase extends AbstractITCase {
         // ----------------------------------------
         ConnInstanceTO connectorTO = new ConnInstanceTO();
 
-        connectorTO.setLocation(connectorService.read(100L).getLocation());
+        connectorTO.setLocation(connectorService.read(100L, Locale.ENGLISH.getLanguage()).getLocation());
 
         // set connector version
         connectorTO.setVersion(connidSoapVersion);
@@ -688,7 +690,7 @@ public class ConnectorITCase extends AbstractITCase {
         BulkAction bulkAction = new BulkAction();
         bulkAction.setType(BulkAction.Type.DELETE);
 
-        ConnInstanceTO conn = connectorService.read(101L);
+        ConnInstanceTO conn = connectorService.read(101L, Locale.ENGLISH.getLanguage());
 
         conn.setKey(0);
         conn.setDisplayName("forBulk1");
@@ -703,22 +705,22 @@ public class ConnectorITCase extends AbstractITCase {
 
         Iterator<String> iter = bulkAction.getTargets().iterator();
 
-        assertNotNull(connectorService.read(Long.valueOf(iter.next())));
-        assertNotNull(connectorService.read(Long.valueOf(iter.next())));
+        assertNotNull(connectorService.read(Long.valueOf(iter.next()), Locale.ENGLISH.getLanguage()));
+        assertNotNull(connectorService.read(Long.valueOf(iter.next()), Locale.ENGLISH.getLanguage()));
 
         connectorService.bulk(bulkAction);
 
         iter = bulkAction.getTargets().iterator();
 
         try {
-            connectorService.read(Long.valueOf(iter.next()));
+            connectorService.read(Long.valueOf(iter.next()), Locale.ENGLISH.getLanguage());
             fail();
         } catch (SyncopeClientException e) {
             assertNotNull(e);
         }
 
         try {
-            connectorService.read(Long.valueOf(iter.next()));
+            connectorService.read(Long.valueOf(iter.next()), Locale.ENGLISH.getLanguage());
             fail();
         } catch (SyncopeClientException e) {
             assertNotNull(e);
@@ -727,13 +729,14 @@ public class ConnectorITCase extends AbstractITCase {
 
     @Test
     public void issueSYNCOPE605() {
-        ConnInstanceTO connectorInstanceTO = connectorService.read(103L);
+        ConnInstanceTO connectorInstanceTO = connectorService.read(103L, Locale.ENGLISH.getLanguage());
         assertTrue(connectorInstanceTO.getCapabilities().isEmpty());
 
         connectorInstanceTO.getCapabilities().add(ConnectorCapability.SEARCH);
         connectorService.update(connectorInstanceTO);
 
-        ConnInstanceTO updatedCapabilities = connectorService.read(connectorInstanceTO.getKey());
+        ConnInstanceTO updatedCapabilities = connectorService.read(
+                connectorInstanceTO.getKey(), Locale.ENGLISH.getLanguage());
         assertNotNull(updatedCapabilities.getCapabilities());
         assertTrue(updatedCapabilities.getCapabilities().size() == 1);
     }

http://git-wip-us.apache.org/repos/asf/syncope/blob/213fbc60/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/MultitenancyITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/MultitenancyITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/MultitenancyITCase.java
index 9ca7b50..c78f47b 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/MultitenancyITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/MultitenancyITCase.java
@@ -26,6 +26,7 @@ import static org.junit.Assert.fail;
 
 import java.security.AccessControlException;
 import java.util.List;
+import java.util.Locale;
 import javax.ws.rs.core.Response;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.client.lib.SyncopeClient;
@@ -127,7 +128,7 @@ public class MultitenancyITCase extends AbstractITCase {
     @Test
     public void createResourceAndSync() {
         // read connector
-        ConnInstanceTO conn = adminClient.getService(ConnectorService.class).read(100L);
+        ConnInstanceTO conn = adminClient.getService(ConnectorService.class).read(100L, Locale.ENGLISH.getLanguage());
         assertNotNull(conn);
         assertEquals("LDAP", conn.getDisplayName());
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/213fbc60/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java
index 4604731..324a10d 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/SyncTaskITCase.java
@@ -27,6 +27,7 @@ import static org.junit.Assert.fail;
 
 import java.util.HashSet;
 import java.util.List;
+import java.util.Locale;
 import java.util.Set;
 import javax.ws.rs.core.Response;
 import org.apache.commons.collections4.CollectionUtils;
@@ -723,7 +724,8 @@ public class SyncTaskITCase extends AbstractTaskITCase {
 
         // 5. Update the LDAP Connector to retrieve passwords
         ResourceTO ldapResource = resourceService.read(RESOURCE_NAME_LDAP);
-        ConnInstanceTO resourceConnector = connectorService.read(ldapResource.getConnector());
+        ConnInstanceTO resourceConnector = connectorService.read(
+                ldapResource.getConnector(), Locale.ENGLISH.getLanguage());
         ConnConfProperty property = resourceConnector.getConfigurationMap().get("retrievePasswordsWithSearch");
         property.getValues().clear();
         property.getValues().add(Boolean.TRUE);

http://git-wip-us.apache.org/repos/asf/syncope/blob/213fbc60/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/VirAttrITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/VirAttrITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/VirAttrITCase.java
index 92bca46..5644447 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/VirAttrITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/reference/VirAttrITCase.java
@@ -24,6 +24,7 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
 import java.util.Collections;
+import java.util.Locale;
 import java.util.Map;
 import org.apache.commons.lang3.SerializationUtils;
 import org.apache.syncope.common.lib.mod.AttrMod;
@@ -369,8 +370,9 @@ public class VirAttrITCase extends AbstractITCase {
         // 3. force cache expiring without any modification
         // ----------------------------------------
         String jdbcURL = null;
-        ConnInstanceTO connInstanceBean = connectorService.readByResource(RESOURCE_NAME_DBVIRATTR);
-        for (ConnConfProperty prop : connInstanceBean.getConfiguration()) {
+        ConnInstanceTO connInstanceTO = connectorService.readByResource(
+                RESOURCE_NAME_DBVIRATTR, Locale.ENGLISH.getLanguage());
+        for (ConnConfProperty prop : connInstanceTO.getConfiguration()) {
             if ("jdbcUrlTemplate".equals(prop.getSchema().getName())) {
                 jdbcURL = prop.getValues().iterator().next().toString();
                 prop.getValues().clear();
@@ -378,7 +380,7 @@ public class VirAttrITCase extends AbstractITCase {
             }
         }
 
-        connectorService.update(connInstanceBean);
+        connectorService.update(connInstanceTO);
 
         UserMod userMod = new UserMod();
         userMod.setKey(actual.getKey());
@@ -416,14 +418,14 @@ public class VirAttrITCase extends AbstractITCase {
         // ----------------------------------------
         // 5. restore connector
         // ----------------------------------------
-        for (ConnConfProperty prop : connInstanceBean.getConfiguration()) {
+        for (ConnConfProperty prop : connInstanceTO.getConfiguration()) {
             if ("jdbcUrlTemplate".equals(prop.getSchema().getName())) {
                 prop.getValues().clear();
                 prop.getValues().add(jdbcURL);
             }
         }
 
-        connectorService.update(connInstanceBean);
+        connectorService.update(connInstanceTO);
         // ----------------------------------------
 
         actual = userService.read(actual.getKey());


[17/33] syncope git commit: Very basic JAXB test to ensure at least no hidden basic failures

Posted by md...@apache.org.
Very basic JAXB test to ensure at least no hidden basic failures


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

Branch: refs/heads/SYNCOPE-156
Commit: 69d8b7b21760e6129cdbafadcb4b628be03379b4
Parents: b8cadde
Author: Francesco Chicchiriccò <il...@apache.org>
Authored: Thu Aug 20 11:58:25 2015 +0200
Committer: Francesco Chicchiriccò <il...@apache.org>
Committed: Thu Aug 20 11:58:25 2015 +0200

----------------------------------------------------------------------
 .../org/apache/syncope/common/lib/JAXBTest.java | 42 ++++++++++++++++++++
 1 file changed, 42 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/syncope/blob/69d8b7b2/common/lib/src/test/java/org/apache/syncope/common/lib/JAXBTest.java
----------------------------------------------------------------------
diff --git a/common/lib/src/test/java/org/apache/syncope/common/lib/JAXBTest.java b/common/lib/src/test/java/org/apache/syncope/common/lib/JAXBTest.java
new file mode 100644
index 0000000..bf8fcfb
--- /dev/null
+++ b/common/lib/src/test/java/org/apache/syncope/common/lib/JAXBTest.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.common.lib;
+
+import static org.junit.Assert.fail;
+
+import java.io.StringWriter;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.Marshaller;
+import org.apache.syncope.common.lib.report.UserReportletConf;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.junit.Test;
+
+public class JAXBTest {
+
+    @Test
+    public void marshal() {
+        try {
+            JAXBContext context = JAXBContext.newInstance(UserReportletConf.class);
+            Marshaller marshaller = context.createMarshaller();
+            marshaller.marshal(new UserTO(), new StringWriter());
+        } catch (Exception e) {
+            fail();
+        }
+    }
+}