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

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

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/security/SyncopeAuthenticationProvider.java
----------------------------------------------------------------------
diff --git a/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/security/SyncopeAuthenticationProvider.java b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/security/SyncopeAuthenticationProvider.java
new file mode 100644
index 0000000..607deb6
--- /dev/null
+++ b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/security/SyncopeAuthenticationProvider.java
@@ -0,0 +1,296 @@
+/*
+ * 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.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Set;
+import javax.annotation.Resource;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.AuditElements.Result;
+import org.apache.syncope.common.lib.types.CipherAlgorithm;
+import org.apache.syncope.core.persistence.api.dao.ConfDAO;
+import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
+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.AttributableUtil;
+import org.apache.syncope.core.persistence.api.entity.AttributableUtilFactory;
+import org.apache.syncope.core.persistence.api.entity.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.conf.CPlainAttr;
+import org.apache.syncope.core.persistence.api.entity.role.Role;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+import org.apache.syncope.core.provisioning.api.ConnectorFactory;
+import org.apache.syncope.core.misc.AuditManager;
+import org.apache.syncope.core.misc.MappingUtil;
+import org.identityconnectors.framework.common.objects.Uid;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Configurable;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.DisabledException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.transaction.annotation.Transactional;
+
+@Configurable
+public class SyncopeAuthenticationProvider implements AuthenticationProvider {
+
+    /**
+     * Logger.
+     */
+    protected static final Logger LOG = LoggerFactory.getLogger(SyncopeAuthenticationProvider.class);
+
+    @Autowired
+    protected AuditManager auditManager;
+
+    @Autowired
+    protected ConfDAO confDAO;
+
+    @Autowired
+    protected UserDAO userDAO;
+
+    @Autowired
+    protected PolicyDAO policyDAO;
+
+    @Autowired
+    protected ConnectorFactory connFactory;
+
+    @Autowired
+    protected AttributableUtilFactory attrUtilFactory;
+
+    @Resource(name = "adminUser")
+    protected String adminUser;
+
+    @Resource(name = "anonymousUser")
+    protected String anonymousUser;
+
+    protected String adminPassword;
+
+    protected String adminPasswordAlgorithm;
+
+    protected String anonymousKey;
+
+    protected UserDetailsService userDetailsService;
+
+    protected final Encryptor encryptor = Encryptor.getInstance();
+
+    /**
+     * @param adminPassword the adminPassword to set
+     */
+    public void setAdminPassword(final String adminPassword) {
+        this.adminPassword = adminPassword;
+    }
+
+    /**
+     * @param adminPasswordAlgorithm the adminPasswordAlgorithm to set
+     */
+    public void setAdminPasswordAlgorithm(final String adminPasswordAlgorithm) {
+        this.adminPasswordAlgorithm = adminPasswordAlgorithm;
+    }
+
+    /**
+     * @param anonymousKey the anonymousKey to set
+     */
+    public void setAnonymousKey(final String anonymousKey) {
+        this.anonymousKey = anonymousKey;
+    }
+
+    public void setUserDetailsService(final UserDetailsService syncopeUserDetailsService) {
+        this.userDetailsService = syncopeUserDetailsService;
+    }
+
+    @Override
+    @Transactional(noRollbackFor = { BadCredentialsException.class, DisabledException.class })
+    public Authentication authenticate(final Authentication authentication)
+            throws AuthenticationException {
+
+        boolean authenticated = false;
+        User user = null;
+
+        String username = authentication.getName();
+        if (anonymousUser.equals(username)) {
+            authenticated = authentication.getCredentials().toString().equals(anonymousKey);
+        } else if (adminUser.equals(username)) {
+            authenticated = encryptor.verify(
+                    authentication.getCredentials().toString(),
+                    CipherAlgorithm.valueOf(adminPasswordAlgorithm),
+                    adminPassword);
+        } else {
+            user = userDAO.find(username);
+
+            if (user != null) {
+                if (user.isSuspended() != null && user.isSuspended()) {
+                    throw new DisabledException("User " + user.getUsername() + " is suspended");
+                }
+
+                CPlainAttr authStatuses = confDAO.find("authentication.statuses");
+                if (authStatuses != null && !authStatuses.getValuesAsStrings().contains(user.getStatus())) {
+                    throw new DisabledException("User " + user.getUsername() + " not allowed to authenticate");
+                }
+
+                authenticated = authenticate(user, authentication.getCredentials().toString());
+
+                updateLoginAttributes(user, authenticated);
+            }
+        }
+
+        UsernamePasswordAuthenticationToken token;
+        if (authenticated) {
+            token = new UsernamePasswordAuthenticationToken(
+                    authentication.getPrincipal(),
+                    null,
+                    userDetailsService.loadUserByUsername(authentication.getPrincipal().toString()).getAuthorities());
+
+            token.setDetails(authentication.getDetails());
+
+            auditManager.audit(
+                    AuditElements.EventCategoryType.REST,
+                    "AuthenticationController",
+                    null,
+                    "login",
+                    Result.SUCCESS,
+                    null,
+                    authenticated,
+                    authentication,
+                    "Successfully authenticated, with roles: " + token.getAuthorities());
+
+            LOG.debug("User {} successfully authenticated, with roles {}",
+                    authentication.getPrincipal(), token.getAuthorities());
+        } else {
+            auditManager.audit(
+                    AuditElements.EventCategoryType.REST,
+                    "AuthenticationController",
+                    null,
+                    "login",
+                    Result.FAILURE,
+                    null,
+                    authenticated,
+                    authentication,
+                    "User " + authentication.getPrincipal() + " not authenticated");
+
+            LOG.debug("User {} not authenticated", authentication.getPrincipal());
+
+            throw new BadCredentialsException("User " + authentication.getPrincipal() + " not authenticated");
+        }
+
+        return token;
+    }
+
+    protected void updateLoginAttributes(User user, boolean authenticated) {
+        boolean userModified = false;
+
+        if (authenticated) {
+            if (confDAO.find("log.lastlogindate", Boolean.toString(true)).getValues().get(0).getBooleanValue()) {
+                user.setLastLoginDate(new Date());
+                userModified = true;
+            }
+
+            if (user.getFailedLogins() != 0) {
+                user.setFailedLogins(0);
+                userModified = true;
+            }
+        } else {
+            user.setFailedLogins(user.getFailedLogins() + 1);
+            userModified = true;
+        }
+
+        if (userModified) {
+            userDAO.save(user);
+        }
+    }
+
+    protected Set<? extends ExternalResource> getPassthroughResources(final User user) {
+        Set<? extends ExternalResource> result = null;
+
+        // 1. look for directly assigned resources, pick the ones whose account policy has authentication resources
+        for (ExternalResource resource : user.getOwnResources()) {
+            if (resource.getAccountPolicy() != null && !resource.getAccountPolicy().getResources().isEmpty()) {
+                if (result == null) {
+                    result = resource.getAccountPolicy().getResources();
+                } else {
+                    result.retainAll(resource.getAccountPolicy().getResources());
+                }
+            }
+        }
+
+        // 2. look for owned roles, pick the ones whose account policy has authentication resources
+        for (Role role : user.getRoles()) {
+            if (role.getAccountPolicy() != null && !role.getAccountPolicy().getResources().isEmpty()) {
+                if (result == null) {
+                    result = role.getAccountPolicy().getResources();
+                } else {
+                    result.retainAll(role.getAccountPolicy().getResources());
+                }
+            }
+        }
+
+        // 3. look for global account policy (if defined)
+        AccountPolicy global = policyDAO.getGlobalAccountPolicy();
+        if (global != null && !global.getResources().isEmpty()) {
+            if (result == null) {
+                result = global.getResources();
+            } else {
+                result.retainAll(global.getResources());
+            }
+        }
+
+        if (result == null) {
+            result = Collections.emptySet();
+        }
+
+        return result;
+    }
+
+    protected boolean authenticate(final User user, final String password) {
+        boolean authenticated = encryptor.verify(password, user.getCipherAlgorithm(), user.getPassword());
+        LOG.debug("{} authenticated on internal storage: {}", user.getUsername(), authenticated);
+
+        final AttributableUtil attrUtil = attrUtilFactory.getInstance(AttributableType.USER);
+        for (Iterator<? extends ExternalResource> itor = getPassthroughResources(user).iterator();
+                itor.hasNext() && !authenticated;) {
+
+            ExternalResource resource = itor.next();
+            String accountId = null;
+            try {
+                accountId = MappingUtil.getAccountIdValue(user, resource, attrUtil.getAccountIdItem(resource));
+                Uid uid = connFactory.getConnector(resource).authenticate(accountId, password, null);
+                if (uid != null) {
+                    authenticated = true;
+                }
+            } catch (Exception e) {
+                LOG.debug("Could not authenticate {} on {}", user.getUsername(), resource.getKey(), e);
+            }
+            LOG.debug("{} authenticated on {} as {}: {}",
+                    user.getUsername(), resource.getKey(), accountId, authenticated);
+        }
+
+        return authenticated;
+    }
+
+    @Override
+    public boolean supports(final Class<? extends Object> type) {
+        return type.equals(UsernamePasswordAuthenticationToken.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/security/SyncopeUserDetailsService.java
----------------------------------------------------------------------
diff --git a/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/security/SyncopeUserDetailsService.java b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/security/SyncopeUserDetailsService.java
new file mode 100644
index 0000000..16eecf7
--- /dev/null
+++ b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/security/SyncopeUserDetailsService.java
@@ -0,0 +1,102 @@
+/*
+ * 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.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.Resource;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.core.persistence.api.RoleEntitlementUtil;
+import org.apache.syncope.core.persistence.api.dao.EntitlementDAO;
+import org.apache.syncope.core.persistence.api.dao.RoleDAO;
+import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.persistence.api.entity.Entitlement;
+import org.apache.syncope.core.persistence.api.entity.role.Role;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Configurable;
+import org.springframework.dao.DataAccessException;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+
+@Configurable
+public class SyncopeUserDetailsService implements UserDetailsService {
+
+    @Autowired
+    protected UserDAO userDAO;
+
+    @Autowired
+    protected RoleDAO roleDAO;
+
+    @Autowired
+    protected EntitlementDAO entitlementDAO;
+
+    @Resource(name = "adminUser")
+    protected String adminUser;
+
+    @Resource(name = "anonymousUser")
+    protected String anonymousUser;
+
+    @Override
+    public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException, DataAccessException {
+        final Set<SimpleGrantedAuthority> authorities = new HashSet<>();
+        if (anonymousUser.equals(username)) {
+            authorities.add(new SimpleGrantedAuthority(SyncopeConstants.ANONYMOUS_ENTITLEMENT));
+        } else if (adminUser.equals(username)) {
+            for (Entitlement entitlement : entitlementDAO.findAll()) {
+                authorities.add(new SimpleGrantedAuthority(entitlement.getKey()));
+            }
+        } else {
+            org.apache.syncope.core.persistence.api.entity.user.User user = userDAO.find(username);
+
+            if (user == null) {
+                throw new UsernameNotFoundException("Could not find any user with id " + username);
+            }
+
+            // Give entitlements based on roles assigned to user (and their ancestors)
+            final Set<Role> roles = new HashSet<>(user.getRoles());
+            for (Role role : user.getRoles()) {
+                roles.addAll(roleDAO.findAncestors(role));
+            }
+            for (Role role : roles) {
+                for (Entitlement entitlement : role.getEntitlements()) {
+                    authorities.add(new SimpleGrantedAuthority(entitlement.getKey()));
+                }
+            }
+            // Give role operational entitlements for owned roles
+            List<Role> ownedRoles = roleDAO.findOwnedByUser(user.getKey());
+            if (!ownedRoles.isEmpty()) {
+                authorities.add(new SimpleGrantedAuthority("ROLE_CREATE"));
+                authorities.add(new SimpleGrantedAuthority("ROLE_READ"));
+                authorities.add(new SimpleGrantedAuthority("ROLE_UPDATE"));
+                authorities.add(new SimpleGrantedAuthority("ROLE_DELETE"));
+
+                for (Role role : ownedRoles) {
+                    authorities.add(new SimpleGrantedAuthority(
+                            RoleEntitlementUtil.getEntitlementNameFromRoleKey(role.getKey())));
+                }
+            }
+        }
+
+        return new User(username, "<PASSWORD_PLACEHOLDER>", true, true, true, true, authorities);
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/security/UnauthorizedRoleException.java
----------------------------------------------------------------------
diff --git a/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/security/UnauthorizedRoleException.java b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/security/UnauthorizedRoleException.java
new file mode 100644
index 0000000..8c29871
--- /dev/null
+++ b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/security/UnauthorizedRoleException.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.syncope.core.misc.security;
+
+import java.util.Collections;
+import java.util.Set;
+
+public class UnauthorizedRoleException extends RuntimeException {
+
+    private static final long serialVersionUID = 7540587364235915081L;
+
+    private final Set<Long> roleIds;
+
+    public UnauthorizedRoleException(final Set<Long> roleIds) {
+        super("Missing entitlement for role(s) " + roleIds);
+        this.roleIds = roleIds;
+    }
+
+    public UnauthorizedRoleException(final Long roleId) {
+        this(Collections.singleton(roleId));
+    }
+
+    public Set<Long> getRoleIds() {
+        return roleIds;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/AttributeDeserializer.java
----------------------------------------------------------------------
diff --git a/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/AttributeDeserializer.java b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/AttributeDeserializer.java
new file mode 100644
index 0000000..3da92ee
--- /dev/null
+++ b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/AttributeDeserializer.java
@@ -0,0 +1,84 @@
+/*
+ * 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.serialization;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import org.apache.commons.lang3.StringUtils;
+import org.identityconnectors.common.Base64;
+import org.identityconnectors.common.security.GuardedString;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.AttributeBuilder;
+import org.identityconnectors.framework.common.objects.Name;
+import org.identityconnectors.framework.common.objects.Uid;
+
+class AttributeDeserializer extends JsonDeserializer<Attribute> {
+
+    @Override
+    public Attribute deserialize(final JsonParser jp, final DeserializationContext ctx)
+            throws IOException, JsonProcessingException {
+
+        ObjectNode tree = jp.readValueAsTree();
+
+        String name = tree.get("name").asText();
+
+        List<Object> values = new ArrayList<Object>();
+        for (Iterator<JsonNode> itor = tree.get("value").iterator(); itor.hasNext();) {
+            JsonNode node = itor.next();
+            if (node.isNull()) {
+                values.add(null);
+            } else if (node.isObject()) {
+                values.add(((ObjectNode) node).traverse(jp.getCodec()).readValueAs(GuardedString.class));
+            } else if (node.isBoolean()) {
+                values.add(node.asBoolean());
+            } else if (node.isDouble()) {
+                values.add(node.asDouble());
+            } else if (node.isLong()) {
+                values.add(node.asLong());
+            } else if (node.isInt()) {
+                values.add(node.asInt());
+            } else {
+                String text = node.asText();
+                if (text.startsWith(AttributeSerializer.BYTE_ARRAY_PREFIX)
+                        && text.endsWith(AttributeSerializer.BYTE_ARRAY_SUFFIX)) {
+
+                    values.add(Base64.decode(StringUtils.substringBetween(
+                            text, AttributeSerializer.BYTE_ARRAY_PREFIX, AttributeSerializer.BYTE_ARRAY_SUFFIX)));
+                } else {
+                    values.add(text);
+                }
+            }
+        }
+
+        return Uid.NAME.equals(name)
+                ? new Uid(values.isEmpty() || values.get(0) == null ? null : values.get(0).toString())
+                : Name.NAME.equals(name)
+                ? new Name(values.isEmpty() || values.get(0) == null ? null : values.get(0).toString())
+                : AttributeBuilder.build(name, values);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/AttributeSerializer.java
----------------------------------------------------------------------
diff --git a/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/AttributeSerializer.java b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/AttributeSerializer.java
new file mode 100644
index 0000000..2fff021
--- /dev/null
+++ b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/AttributeSerializer.java
@@ -0,0 +1,78 @@
+/*
+ * 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.serialization;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import java.io.IOException;
+import org.identityconnectors.common.Base64;
+import org.identityconnectors.common.security.GuardedString;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class AttributeSerializer extends JsonSerializer<Attribute> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AttributeSerializer.class);
+
+    public static final String BYTE_ARRAY_PREFIX = "<binary>";
+
+    public static final String BYTE_ARRAY_SUFFIX = "</binary>";
+
+    @Override
+    public void serialize(final Attribute source, final JsonGenerator jgen, final SerializerProvider sp)
+            throws IOException, JsonProcessingException {
+
+        jgen.writeStartObject();
+
+        jgen.writeStringField("name", source.getName());
+
+        jgen.writeFieldName("value");
+        if (source.getValue() == null) {
+            jgen.writeNull();
+        } else {
+            jgen.writeStartArray();
+            for (Object value : source.getValue()) {
+                if (value == null) {
+                    jgen.writeNull();
+                } else if (value instanceof GuardedString) {
+                    jgen.writeObject(value);
+                } else if (value instanceof Integer) {
+                    jgen.writeNumber((Integer) value);
+                } else if (value instanceof Long) {
+                    jgen.writeNumber((Long) value);
+                } else if (value instanceof Double) {
+                    jgen.writeNumber((Double) value);
+                } else if (value instanceof Boolean) {
+                    jgen.writeBoolean((Boolean) value);
+                } else if (value instanceof byte[]) {
+                    jgen.writeString(BYTE_ARRAY_PREFIX + Base64.encode((byte[]) value) + BYTE_ARRAY_SUFFIX);
+                } else {
+                    jgen.writeString(value.toString());
+                }
+            }
+            jgen.writeEndArray();
+        }
+
+        jgen.writeEndObject();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/GuardedStringDeserializer.java
----------------------------------------------------------------------
diff --git a/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/GuardedStringDeserializer.java b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/GuardedStringDeserializer.java
new file mode 100644
index 0000000..d8c372d
--- /dev/null
+++ b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/GuardedStringDeserializer.java
@@ -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.
+ */
+package org.apache.syncope.core.misc.serialization;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import org.identityconnectors.common.Base64;
+import org.identityconnectors.common.security.EncryptorFactory;
+import org.identityconnectors.common.security.GuardedString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class GuardedStringDeserializer extends JsonDeserializer<GuardedString> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(GuardedStringDeserializer.class);
+
+    @Override
+    public GuardedString deserialize(final JsonParser jp, final DeserializationContext ctx)
+            throws IOException, JsonProcessingException {
+
+        ObjectNode tree = jp.readValueAsTree();
+
+        boolean readOnly = false;
+        if (tree.has("readOnly")) {
+            readOnly = tree.get("readOnly").asBoolean();
+        }
+        boolean disposed = false;
+        if (tree.has("disposed")) {
+            disposed = tree.get("disposed").asBoolean();
+        }
+        byte[] encryptedBytes = null;
+        if (tree.has("encryptedBytes")) {
+            encryptedBytes = Base64.decode(tree.get("encryptedBytes").asText());
+        }
+        String base64SHA1Hash = null;
+        if (tree.has("base64SHA1Hash")) {
+            base64SHA1Hash = tree.get("base64SHA1Hash").asText();
+        }
+
+        final byte[] clearBytes = EncryptorFactory.getInstance().getDefaultEncryptor().decrypt(encryptedBytes);
+
+        GuardedString dest = new GuardedString(new String(clearBytes).toCharArray());
+
+        try {
+            Field field = GuardedString.class.getDeclaredField("readOnly");
+            field.setAccessible(true);
+            field.setBoolean(dest, readOnly);
+        } catch (Exception e) {
+            LOG.error("Could not set field value to {}", readOnly, e);
+        }
+
+        try {
+            Field field = GuardedString.class.getDeclaredField("disposed");
+            field.setAccessible(true);
+            field.setBoolean(dest, disposed);
+        } catch (Exception e) {
+            LOG.error("Could not set field value to {}", disposed, e);
+        }
+
+        if (base64SHA1Hash != null) {
+            try {
+                Field field = GuardedString.class.getDeclaredField("base64SHA1Hash");
+                field.setAccessible(true);
+                field.set(dest, base64SHA1Hash);
+            } catch (Exception e) {
+                LOG.error("Could not set field value to {}", base64SHA1Hash, e);
+            }
+        }
+
+        return dest;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/GuardedStringSerializer.java
----------------------------------------------------------------------
diff --git a/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/GuardedStringSerializer.java b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/GuardedStringSerializer.java
new file mode 100644
index 0000000..de9f60a
--- /dev/null
+++ b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/GuardedStringSerializer.java
@@ -0,0 +1,90 @@
+/*
+ * 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.serialization;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import org.identityconnectors.common.Base64;
+import org.identityconnectors.common.security.EncryptorFactory;
+import org.identityconnectors.common.security.GuardedString;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class GuardedStringSerializer extends JsonSerializer<GuardedString> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(GuardedStringSerializer.class);
+
+    @Override
+    public void serialize(final GuardedString source, final JsonGenerator jgen, final SerializerProvider sp)
+            throws IOException, JsonProcessingException {
+
+        jgen.writeStartObject();
+
+        boolean readOnly = false;
+        try {
+            Field field = GuardedString.class.getDeclaredField("readOnly");
+            field.setAccessible(true);
+            readOnly = field.getBoolean(source);
+        } catch (Exception e) {
+            LOG.error("Could not get field value", e);
+        }
+        jgen.writeBooleanField("readOnly", readOnly);
+
+        boolean disposed = false;
+        try {
+            Field field = GuardedString.class.getDeclaredField("disposed");
+            field.setAccessible(true);
+            disposed = field.getBoolean(source);
+        } catch (Exception e) {
+            LOG.error("Could not get field value", e);
+        }
+        jgen.writeBooleanField("disposed", disposed);
+
+        final StringBuilder cleartext = new StringBuilder();
+        ((GuardedString) source).access(new GuardedString.Accessor() {
+
+            @Override
+            public void access(final char[] clearChars) {
+                cleartext.append(clearChars);
+            }
+        });
+        final byte[] encryptedBytes =
+                EncryptorFactory.getInstance().getDefaultEncryptor().encrypt(cleartext.toString().getBytes());
+        jgen.writeStringField("encryptedBytes", Base64.encode(encryptedBytes));
+
+        String base64SHA1Hash = null;
+        try {
+            Field field = GuardedString.class.getDeclaredField("base64SHA1Hash");
+            field.setAccessible(true);
+            base64SHA1Hash = field.get(source).toString();
+        } catch (Exception e) {
+            LOG.error("Could not get field value", e);
+        }
+        if (base64SHA1Hash != null) {
+            jgen.writeStringField("base64SHA1Hash", base64SHA1Hash);
+        }
+
+        jgen.writeEndObject();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/POJOHelper.java
----------------------------------------------------------------------
diff --git a/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/POJOHelper.java b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/POJOHelper.java
new file mode 100644
index 0000000..dd42568
--- /dev/null
+++ b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/POJOHelper.java
@@ -0,0 +1,80 @@
+/*
+ * 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.serialization;
+
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.module.afterburner.AfterburnerModule;
+import org.identityconnectors.common.security.GuardedString;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.SyncToken;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Helper class for serialization and deserialization of configuration objects (POJOs) in JSON.
+ */
+public final class POJOHelper {
+
+    private static final Logger LOG = LoggerFactory.getLogger(POJOHelper.class);
+
+    private static final ObjectMapper MAPPER;
+
+    static {
+        SimpleModule pojoModule = new SimpleModule("POJOModule", new Version(1, 0, 0, null, null, null));
+        pojoModule.addSerializer(GuardedString.class, new GuardedStringSerializer());
+        pojoModule.addSerializer(Attribute.class, new AttributeSerializer());
+        pojoModule.addSerializer(SyncToken.class, new SyncTokenSerializer());
+        pojoModule.addDeserializer(GuardedString.class, new GuardedStringDeserializer());
+        pojoModule.addDeserializer(Attribute.class, new AttributeDeserializer());
+        pojoModule.addDeserializer(SyncToken.class, new SyncTokenDeserializer());
+
+        MAPPER = new ObjectMapper();
+        MAPPER.registerModule(pojoModule);
+        MAPPER.registerModule(new AfterburnerModule());
+    }
+
+    public static String serialize(final Object object) {
+        String result = null;
+
+        try {
+            result = MAPPER.writeValueAsString(object);
+        } catch (Exception e) {
+            LOG.error("During serialization", e);
+        }
+
+        return result;
+    }
+
+    public static <T extends Object> T deserialize(final String serialized, final Class<T> reference) {
+        T result = null;
+
+        try {
+            result = MAPPER.readValue(serialized, reference);
+        } catch (Exception e) {
+            LOG.error("During deserialization", e);
+        }
+
+        return result;
+    }
+
+    private POJOHelper() {
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/SyncTokenDeserializer.java
----------------------------------------------------------------------
diff --git a/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/SyncTokenDeserializer.java b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/SyncTokenDeserializer.java
new file mode 100644
index 0000000..3497373
--- /dev/null
+++ b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/SyncTokenDeserializer.java
@@ -0,0 +1,65 @@
+/*
+ * 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.serialization;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import java.io.IOException;
+import org.apache.commons.codec.binary.Base64;
+import org.identityconnectors.framework.common.objects.SyncToken;
+
+class SyncTokenDeserializer extends JsonDeserializer<SyncToken> {
+
+    @Override
+    public SyncToken deserialize(final JsonParser jp, final DeserializationContext ctx)
+            throws IOException, JsonProcessingException {
+
+        ObjectNode tree = jp.readValueAsTree();
+
+        Object value = null;
+        if (tree.has("value")) {
+            JsonNode node = tree.get("value");
+            value = node.isNull()
+                    ? null
+                    : node.isBoolean()
+                            ? node.asBoolean()
+                            : node.isDouble()
+                                    ? node.asDouble()
+                                    : node.isLong()
+                                            ? node.asLong()
+                                            : node.isInt()
+                                                    ? node.asInt()
+                                                    : node.asText();
+
+            if (value instanceof String) {
+                String base64 = (String) value;
+                if (Base64.isBase64(base64)) {
+                    value = Base64.decodeBase64(base64);
+                }
+            }
+        }
+
+        return new SyncToken(value);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/SyncTokenSerializer.java
----------------------------------------------------------------------
diff --git a/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/SyncTokenSerializer.java b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/SyncTokenSerializer.java
new file mode 100644
index 0000000..c2f2183
--- /dev/null
+++ b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/SyncTokenSerializer.java
@@ -0,0 +1,58 @@
+/*
+ * 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.serialization;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import java.io.IOException;
+import org.apache.commons.codec.binary.Base64;
+import org.identityconnectors.framework.common.objects.SyncToken;
+
+class SyncTokenSerializer extends JsonSerializer<SyncToken> {
+
+    @Override
+    public void serialize(final SyncToken source, final JsonGenerator jgen, final SerializerProvider sp)
+            throws IOException, JsonProcessingException {
+
+        jgen.writeStartObject();
+
+        jgen.writeFieldName("value");
+
+        if (source.getValue() == null) {
+            jgen.writeNull();
+        } else if (source.getValue() instanceof Boolean) {
+            jgen.writeBoolean((Boolean) source.getValue());
+        } else if (source.getValue() instanceof Double) {
+            jgen.writeNumber((Double) source.getValue());
+        } else if (source.getValue() instanceof Long) {
+            jgen.writeNumber((Long) source.getValue());
+        } else if (source.getValue() instanceof Integer) {
+            jgen.writeNumber((Integer) source.getValue());
+        } else if (source.getValue() instanceof byte[]) {
+            jgen.writeString(Base64.encodeBase64String((byte[]) source.getValue()));
+        } else {
+            jgen.writeString(source.getValue().toString());
+        }
+
+        jgen.writeEndObject();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/UnwrappedObjectMapper.java
----------------------------------------------------------------------
diff --git a/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/UnwrappedObjectMapper.java b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/UnwrappedObjectMapper.java
new file mode 100644
index 0000000..b79b6bd
--- /dev/null
+++ b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/serialization/UnwrappedObjectMapper.java
@@ -0,0 +1,95 @@
+/*
+ * 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.serialization;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.util.Map;
+
+/**
+ * Jackson ObjectMapper that unwraps singleton map values and enable default
+ * typing for handling abstract types serialization.
+ */
+public class UnwrappedObjectMapper extends ObjectMapper {
+
+    private static final long serialVersionUID = -317191546835195103L;
+
+    /**
+     * Unwraps the given value if it implements the Map interface and contains
+     * only a single entry. Otherwise the value is returned unmodified.
+     *
+     * @param value the potential Map to unwrap
+     * @return the unwrapped map or the original value
+     */
+    private Object unwrapMap(final Object value) {
+        if (value instanceof Map) {
+            Map<?, ?> map = (Map<?, ?>) value;
+            if (map.size() == 1) {
+                return map.values().iterator().next();
+            }
+        }
+
+        return value;
+    }
+
+    @Override
+    public void writeValue(final JsonGenerator jgen, final Object value)
+            throws IOException, JsonGenerationException, JsonMappingException {
+
+        super.writeValue(jgen, unwrapMap(value));
+    }
+
+    @Override
+    public void writeValue(final File resultFile, final Object value)
+            throws IOException, JsonGenerationException, JsonMappingException {
+
+        super.writeValue(resultFile, unwrapMap(value));
+    }
+
+    @Override
+    public void writeValue(final OutputStream out, final Object value)
+            throws IOException, JsonGenerationException, JsonMappingException {
+
+        super.writeValue(out, unwrapMap(value));
+    }
+
+    @Override
+    public void writeValue(final Writer w, final Object value)
+            throws IOException, JsonGenerationException, JsonMappingException {
+
+        super.writeValue(w, unwrapMap(value));
+    }
+
+    @Override
+    public byte[] writeValueAsBytes(final Object value) throws JsonProcessingException {
+        return super.writeValueAsBytes(unwrapMap(value));
+    }
+
+    @Override
+    public String writeValueAsString(final Object value) throws JsonProcessingException {
+        return super.writeValueAsString(unwrapMap(value));
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/spring/ApplicationContextProvider.java
----------------------------------------------------------------------
diff --git a/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/spring/ApplicationContextProvider.java b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/spring/ApplicationContextProvider.java
new file mode 100644
index 0000000..dfbcbe4
--- /dev/null
+++ b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/spring/ApplicationContextProvider.java
@@ -0,0 +1,47 @@
+/*
+ * 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.spring;
+
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ConfigurableApplicationContext;
+
+public class ApplicationContextProvider implements ApplicationContextAware {
+
+    private static ConfigurableApplicationContext ctx;
+
+    public static ConfigurableApplicationContext getApplicationContext() {
+        return ctx;
+    }
+
+    public static DefaultListableBeanFactory getBeanFactory() {
+        return (DefaultListableBeanFactory) ctx.getBeanFactory();
+    }
+
+    /**
+     * Wiring the ApplicationContext into a static method.
+     *
+     * @param ctx Spring application context
+     */
+    @Override
+    public void setApplicationContext(final ApplicationContext ctx) {
+        ApplicationContextProvider.ctx = (ConfigurableApplicationContext) ctx;
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/spring/BeanUtils.java
----------------------------------------------------------------------
diff --git a/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/spring/BeanUtils.java b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/spring/BeanUtils.java
new file mode 100644
index 0000000..97b57db
--- /dev/null
+++ b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/spring/BeanUtils.java
@@ -0,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.core.misc.spring;
+
+import static org.springframework.beans.BeanUtils.getPropertyDescriptor;
+import static org.springframework.beans.BeanUtils.getPropertyDescriptors;
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.FatalBeanException;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+
+/**
+ * Overrides Spring's BeanUtils not using collection setters but instead getters + addAll() / putAll(),
+ * in a JAXB friendly way.
+ *
+ * Refer to <a href="https://issues.apache.org/jira/browse/SYNCOPE-246">SYNCOPE-246</a> for more information.
+ *
+ * @see org.springframework.beans.BeanUtils
+ */
+public final class BeanUtils {
+
+    private BeanUtils() {
+        // Empty private constructor for static utility classes
+    }
+
+    /**
+     * Copy the property values of the given source bean into the target bean.
+     * <p>
+     * Note: The source and target classes do not have to match or even be derived
+     * from each other, as long as the properties match. Any bean properties that the
+     * source bean exposes but the target bean does not will silently be ignored.
+     * </p><p>
+     * This is just a convenience method. For more complex transfer needs,
+     * consider using a full BeanWrapper.
+     * </p>
+     * @param source the source bean
+     * @param target the target bean
+     * @throws BeansException if the copying failed
+     * @see org.springframework.beans.BeanWrapper
+     */
+    public static void copyProperties(final Object source, final Object target) throws BeansException {
+        copyProperties(source, target, null, (String[]) null);
+    }
+
+    /**
+     * Copy the property values of the given source bean into the given target bean,
+     * only setting properties defined in the given "editable" class (or interface).
+     * <p>
+     * Note: The source and target classes do not have to match or even be derived
+     * from each other, as long as the properties match. Any bean properties that the
+     * source bean exposes but the target bean does not will silently be ignored.
+     * </p><p>
+     * This is just a convenience method. For more complex transfer needs,
+     * consider using a full BeanWrapper.
+     * </p>
+     *
+     * @param source the source bean
+     * @param target the target bean
+     * @param editable the class (or interface) to restrict property setting to
+     * @throws BeansException if the copying failed
+     * @see org.springframework.beans.BeanWrapper
+     */
+    public static void copyProperties(final Object source, final Object target, final Class<?> editable)
+            throws BeansException {
+
+        copyProperties(source, target, editable, (String[]) null);
+    }
+
+    /**
+     * Copy the property values of the given source bean into the given target bean,
+     * ignoring the given "ignoreProperties".
+     * <p>
+     * Note: The source and target classes do not have to match or even be derived
+     * from each other, as long as the properties match. Any bean properties that the
+     * source bean exposes but the target bean does not will silently be ignored.
+     * </p><p>
+     * This is just a convenience method. For more complex transfer needs,
+     * consider using a full BeanWrapper.
+     * </p>
+     *
+     * @param source the source bean
+     * @param target the target bean
+     * @param ignoreProperties array of property names to ignore
+     * @throws BeansException if the copying failed
+     * @see org.springframework.beans.BeanWrapper
+     */
+    public static void copyProperties(final Object source, final Object target, final String... ignoreProperties)
+            throws BeansException {
+
+        copyProperties(source, target, null, ignoreProperties);
+    }
+
+    /**
+     * Copy the property values of the given source bean into the given target bean.
+     * <p>
+     * Note: The source and target classes do not have to match or even be derived
+     * from each other, as long as the properties match. Any bean properties that the
+     * source bean exposes but the target bean does not will silently be ignored.
+     * </p>
+     *
+     * @param source the source bean
+     * @param target the target bean
+     * @param editable the class (or interface) to restrict property setting to
+     * @param ignoreProperties array of property names to ignore
+     * @throws BeansException if the copying failed
+     * @see org.springframework.beans.BeanWrapper
+     */
+    @SuppressWarnings("unchecked")
+    private static void copyProperties(final Object source, final Object target, final Class<?> editable,
+            final String... ignoreProperties) throws BeansException {
+
+        Assert.notNull(source, "Source must not be null");
+        Assert.notNull(target, "Target must not be null");
+
+        Class<?> actualEditable = target.getClass();
+        if (editable != null) {
+            if (!editable.isInstance(target)) {
+                throw new IllegalArgumentException("Target class [" + target.getClass().getName()
+                        + "] not assignable to Editable class [" + editable.getName() + "]");
+            }
+            actualEditable = editable;
+        }
+        PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
+        List<String> ignoreList = (ignoreProperties == null)
+                ? Collections.<String>emptyList() : Arrays.asList(ignoreProperties);
+
+        for (PropertyDescriptor targetPd : targetPds) {
+            if (ignoreProperties == null || (!ignoreList.contains(targetPd.getName()))) {
+                PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
+                if (sourcePd != null) {
+                    Method readMethod = sourcePd.getReadMethod();
+                    if (readMethod != null) {
+                        Method writeMethod = targetPd.getWriteMethod();
+
+                        try {
+                            // Diverts from Spring's BeanUtils: if no write method is found and property is collection,
+                            // try to use addAll() / putAll().
+                            if (writeMethod == null) {
+                                Object value = readMethod.invoke(source);
+                                Method targetReadMethod = targetPd.getReadMethod();
+                                if (targetReadMethod != null) {
+                                    if (!Modifier.isPublic(targetReadMethod.getDeclaringClass().getModifiers())) {
+                                        targetReadMethod.setAccessible(true);
+                                    }
+                                    Object destValue = targetReadMethod.invoke(target);
+
+                                    if (value instanceof Collection && destValue instanceof Collection) {
+                                        ((Collection) destValue).clear();
+                                        ((Collection) destValue).addAll((Collection) value);
+                                    } else if (value instanceof Map && destValue instanceof Map) {
+                                        ((Map) destValue).clear();
+                                        ((Map) destValue).putAll((Map) value);
+                                    }
+                                }
+                            } else if (ClassUtils.isAssignable(
+                                    writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
+
+                                if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
+                                    readMethod.setAccessible(true);
+                                }
+                                Object value = readMethod.invoke(source);
+                                if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
+                                    writeMethod.setAccessible(true);
+                                }
+                                writeMethod.invoke(target, value);
+                            }
+                        } catch (Throwable ex) {
+                            throw new FatalBeanException(
+                                    "Could not copy property '" + targetPd.getName() + "' from source to target", ex);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/spring/ResourceWithFallbackLoader.java
----------------------------------------------------------------------
diff --git a/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/spring/ResourceWithFallbackLoader.java b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/spring/ResourceWithFallbackLoader.java
new file mode 100644
index 0000000..0fdc116
--- /dev/null
+++ b/syncope620/core/misc/src/main/java/org/apache/syncope/core/misc/spring/ResourceWithFallbackLoader.java
@@ -0,0 +1,82 @@
+/*
+ * 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.spring;
+
+import java.io.IOException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.springframework.context.ResourceLoaderAware;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.core.io.support.ResourcePatternResolver;
+
+public class ResourceWithFallbackLoader implements ResourceLoaderAware, ResourcePatternResolver {
+
+    private ResourcePatternResolver resolver;
+
+    private String primary;
+
+    private String fallback;
+
+    @Override
+    public void setResourceLoader(final ResourceLoader resourceLoader) {
+        this.resolver = (ResourcePatternResolver) resourceLoader;
+    }
+
+    public void setPrimary(final String primary) {
+        this.primary = primary;
+    }
+
+    public void setFallback(final String fallback) {
+        this.fallback = fallback;
+    }
+
+    @Override
+    public Resource getResource(final String location) {
+        Resource resource = resolver.getResource(primary + location);
+        if (!resource.exists()) {
+            resource = resolver.getResource(fallback + location);
+        }
+
+        return resource;
+    }
+
+    public Resource getResource() {
+        return getResource(StringUtils.EMPTY);
+    }
+
+    @Override
+    public Resource[] getResources(final String locationPattern) throws IOException {
+        Resource[] resources = resolver.getResources(primary + locationPattern);
+        if (ArrayUtils.isEmpty(resources)) {
+            resources = resolver.getResources(fallback + locationPattern);
+        }
+
+        return resources;
+    }
+
+    public Resource[] getResources() throws IOException {
+        return getResources(StringUtils.EMPTY);
+    }
+
+    @Override
+    public ClassLoader getClassLoader() {
+        return resolver.getClassLoader();
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/misc/src/main/resources/security.properties
----------------------------------------------------------------------
diff --git a/syncope620/core/misc/src/main/resources/security.properties b/syncope620/core/misc/src/main/resources/security.properties
new file mode 100644
index 0000000..c0fcd37
--- /dev/null
+++ b/syncope620/core/misc/src/main/resources/security.properties
@@ -0,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.
+adminUser=admin
+adminPassword=5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8
+adminPasswordAlgorithm=SHA1
+
+anonymousUser=${anonymousUser}
+anonymousKey=${anonymousKey}
+
+secretKey=${secretKey}
+# default for LDAP / RFC2307 SSHA
+digester.saltIterations=1
+digester.saltSizeBytes=8
+digester.invertPositionOfPlainSaltInEncryptionResults=true
+digester.invertPositionOfSaltInMessageBeforeDigesting=true
+digester.useLenientSaltSizeCheck=true
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/misc/src/main/resources/securityContext.xml
----------------------------------------------------------------------
diff --git a/syncope620/core/misc/src/main/resources/securityContext.xml b/syncope620/core/misc/src/main/resources/securityContext.xml
new file mode 100644
index 0000000..a835970
--- /dev/null
+++ b/syncope620/core/misc/src/main/resources/securityContext.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:security="http://www.springframework.org/schema/security"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+                           http://www.springframework.org/schema/beans/spring-beans.xsd
+                           http://www.springframework.org/schema/security
+                           http://www.springframework.org/schema/security/spring-security.xsd">
+
+  <bean id="adminUser" class="java.lang.String">
+    <constructor-arg value="${adminUser}"/>
+  </bean>
+  <bean id="anonymousUser" class="java.lang.String">
+    <constructor-arg value="${anonymousUser}"/>
+  </bean>
+  
+  <security:global-method-security pre-post-annotations="enabled"/>
+  
+  <bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
+    <security:filter-chain-map path-type="ant">
+      <security:filter-chain pattern="/**" filters="securityContextPersistenceFilter"/>
+    </security:filter-chain-map>
+  </bean>
+  
+  <bean id="securityContextRepository" class='org.springframework.security.web.context.NullSecurityContextRepository'/>
+
+  <bean id="securityContextPersistenceFilter"
+        class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
+    <property name="securityContextRepository" ref="securityContextRepository"/>
+  </bean>
+
+  <security:http security-context-repository-ref="securityContextRepository" realm="Apache Syncope authentication">
+    <security:http-basic/>
+    <security:anonymous username="${anonymousUser}"/>
+    <security:intercept-url pattern="/**"/>
+  </security:http>
+
+  <bean id="syncopeUserDetailsService" class="org.apache.syncope.core.misc.security.SyncopeUserDetailsService"/>
+
+  <bean id="syncopeAuthenticationProvider" class="org.apache.syncope.core.misc.security.SyncopeAuthenticationProvider">
+    <property name="adminPassword" value="${adminPassword}"/>
+    <property name="adminPasswordAlgorithm" value="${adminPasswordAlgorithm}"/>
+    <property name="anonymousKey" value="${anonymousKey}"/>
+    <property name="userDetailsService" ref="syncopeUserDetailsService"/>
+  </bean>
+
+  <security:authentication-manager>
+    <security:authentication-provider ref="syncopeAuthenticationProvider"/>
+  </security:authentication-manager>
+</beans>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/misc/src/test/java/org/apache/syncope/core/misc/search/SearchCondConverterTest.java
----------------------------------------------------------------------
diff --git a/syncope620/core/misc/src/test/java/org/apache/syncope/core/misc/search/SearchCondConverterTest.java b/syncope620/core/misc/src/test/java/org/apache/syncope/core/misc/search/SearchCondConverterTest.java
new file mode 100644
index 0000000..27cd0b6
--- /dev/null
+++ b/syncope620/core/misc/src/test/java/org/apache/syncope/core/misc/search/SearchCondConverterTest.java
@@ -0,0 +1,165 @@
+/*
+ * 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.search;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.syncope.common.lib.search.RoleFiqlSearchConditionBuilder;
+import org.apache.syncope.common.lib.search.SpecialAttr;
+import org.apache.syncope.common.lib.search.UserFiqlSearchConditionBuilder;
+import org.apache.syncope.core.persistence.api.dao.search.AttributeCond;
+import org.apache.syncope.core.persistence.api.dao.search.EntitlementCond;
+import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
+import org.apache.syncope.core.persistence.api.dao.search.ResourceCond;
+import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
+import org.apache.syncope.core.persistence.api.dao.search.SubjectCond;
+import org.junit.Test;
+
+public class SearchCondConverterTest {
+
+    @Test
+    public void eq() {
+        String fiqlExpression = new UserFiqlSearchConditionBuilder().is("username").equalTo("rossini").query();
+        assertEquals("username==rossini", fiqlExpression);
+
+        SubjectCond attrCond = new SubjectCond(AttributeCond.Type.EQ);
+        attrCond.setSchema("username");
+        attrCond.setExpression("rossini");
+        SearchCond simpleCond = SearchCond.getLeafCond(attrCond);
+
+        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+    }
+
+    @Test
+    public void like() {
+        String fiqlExpression = new UserFiqlSearchConditionBuilder().is("username").equalTo("ros*").query();
+        assertEquals("username==ros*", fiqlExpression);
+
+        AttributeCond attrCond = new SubjectCond(AttributeCond.Type.LIKE);
+        attrCond.setSchema("username");
+        attrCond.setExpression("ros%");
+        SearchCond simpleCond = SearchCond.getLeafCond(attrCond);
+
+        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+    }
+
+    @Test
+    public void isNull() {
+        String fiqlExpression = new UserFiqlSearchConditionBuilder().is("loginDate").nullValue().query();
+        assertEquals("loginDate==" + SpecialAttr.NULL, fiqlExpression);
+
+        AttributeCond attrCond = new AttributeCond(AttributeCond.Type.ISNULL);
+        attrCond.setSchema("loginDate");
+        SearchCond simpleCond = SearchCond.getLeafCond(attrCond);
+
+        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+    }
+
+    @Test
+    public void isNotNull() {
+        String fiqlExpression = new UserFiqlSearchConditionBuilder().is("loginDate").notNullValue().query();
+        assertEquals("loginDate!=" + SpecialAttr.NULL, fiqlExpression);
+
+        AttributeCond attrCond = new AttributeCond(AttributeCond.Type.ISNOTNULL);
+        attrCond.setSchema("loginDate");
+        SearchCond simpleCond = SearchCond.getLeafCond(attrCond);
+
+        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+    }
+
+    @Test
+    public void roles() {
+        String fiqlExpression = new UserFiqlSearchConditionBuilder().hasRoles(1L).query();
+        assertEquals(SpecialAttr.ROLES + "==1", fiqlExpression);
+
+        MembershipCond membCond = new MembershipCond();
+        membCond.setRoleId(1L);
+        SearchCond simpleCond = SearchCond.getLeafCond(membCond);
+
+        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+    }
+
+    @Test
+    public void resources() {
+        String fiqlExpression = new UserFiqlSearchConditionBuilder().hasResources("resource-ldap").query();
+        assertEquals(SpecialAttr.RESOURCES + "==resource-ldap", fiqlExpression);
+
+        ResourceCond resCond = new ResourceCond();
+        resCond.setResourceName("resource-ldap");
+        SearchCond simpleCond = SearchCond.getLeafCond(resCond);
+
+        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+    }
+
+    @Test
+    public void entitlements() {
+        String fiqlExpression = new RoleFiqlSearchConditionBuilder().hasEntitlements("USER_LIST").query();
+        assertEquals(SpecialAttr.ENTITLEMENTS + "==USER_LIST", fiqlExpression);
+
+        EntitlementCond entCond = new EntitlementCond();
+        entCond.setExpression("USER_LIST");
+        SearchCond simpleCond = SearchCond.getLeafCond(entCond);
+
+        assertEquals(simpleCond, SearchCondConverter.convert(fiqlExpression));
+    }
+
+    @Test
+    public void and() {
+        String fiqlExpression = new UserFiqlSearchConditionBuilder().
+                is("fullname").equalTo("*o*").and("fullname").equalTo("*i*").query();
+        assertEquals("fullname==*o*;fullname==*i*", fiqlExpression);
+
+        AttributeCond fullnameLeafCond1 = new AttributeCond(AttributeCond.Type.LIKE);
+        fullnameLeafCond1.setSchema("fullname");
+        fullnameLeafCond1.setExpression("%o%");
+        AttributeCond fullnameLeafCond2 = new AttributeCond(AttributeCond.Type.LIKE);
+        fullnameLeafCond2.setSchema("fullname");
+        fullnameLeafCond2.setExpression("%i%");
+        SearchCond andCond = SearchCond.getAndCond(
+                SearchCond.getLeafCond(fullnameLeafCond1),
+                SearchCond.getLeafCond(fullnameLeafCond2));
+
+        assertEquals(andCond, SearchCondConverter.convert(fiqlExpression));
+    }
+
+    @Test
+    public void or() {
+        String fiqlExpression = new UserFiqlSearchConditionBuilder().
+                is("fullname").equalTo("*o*", "*i*", "*ini").query();
+        assertEquals("fullname==*o*,fullname==*i*,fullname==*ini", fiqlExpression);
+
+        AttributeCond fullnameLeafCond1 = new AttributeCond(AttributeCond.Type.LIKE);
+        fullnameLeafCond1.setSchema("fullname");
+        fullnameLeafCond1.setExpression("%o%");
+        AttributeCond fullnameLeafCond2 = new AttributeCond(AttributeCond.Type.LIKE);
+        fullnameLeafCond2.setSchema("fullname");
+        fullnameLeafCond2.setExpression("%i%");
+        AttributeCond fullnameLeafCond3 = new AttributeCond(AttributeCond.Type.LIKE);
+        fullnameLeafCond3.setSchema("fullname");
+        fullnameLeafCond3.setExpression("%ini");
+        SearchCond orCond = SearchCond.getOrCond(
+                SearchCond.getLeafCond(fullnameLeafCond1),
+                SearchCond.getOrCond(
+                        SearchCond.getLeafCond(fullnameLeafCond2),
+                        SearchCond.getLeafCond(fullnameLeafCond3)));
+
+        assertEquals(orCond, SearchCondConverter.convert(fiqlExpression));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/misc/src/test/java/org/apache/syncope/core/misc/security/EncryptorTest.java
----------------------------------------------------------------------
diff --git a/syncope620/core/misc/src/test/java/org/apache/syncope/core/misc/security/EncryptorTest.java b/syncope620/core/misc/src/test/java/org/apache/syncope/core/misc/security/EncryptorTest.java
new file mode 100644
index 0000000..98e8061
--- /dev/null
+++ b/syncope620/core/misc/src/test/java/org/apache/syncope/core/misc/security/EncryptorTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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 static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.syncope.common.lib.types.CipherAlgorithm;
+import org.junit.Test;
+
+/**
+ * Test class to test all encryption algorithms.
+ */
+public class EncryptorTest {
+
+    private final String password = "password";
+
+    private final Encryptor encryptor = Encryptor.getInstance();
+
+    /**
+     * Verify all algorithms.
+     */
+    @Test
+    public void testEncoder() throws Exception {
+        for (CipherAlgorithm cipherAlgorithm : CipherAlgorithm.values()) {
+            final String encPassword = encryptor.encode(password, cipherAlgorithm);
+
+            assertNotNull(encPassword);
+            assertTrue(encryptor.verify(password, cipherAlgorithm, encPassword));
+            assertFalse(encryptor.verify("pass", cipherAlgorithm, encPassword));
+
+            // check that same password encoded with BCRYPT or Salted versions results in different digest
+            if (cipherAlgorithm.equals(CipherAlgorithm.BCRYPT) || cipherAlgorithm.getAlgorithm().startsWith("S-")) {
+                final String encSamePassword = encryptor.encode(password, cipherAlgorithm);
+                assertNotNull(encSamePassword);
+                assertFalse(encSamePassword.equals(encPassword));
+                assertTrue(encryptor.verify(password, cipherAlgorithm, encSamePassword));
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/d30c8526/syncope620/core/misc/src/test/java/org/apache/syncope/core/misc/security/PasswordGeneratorTest.java
----------------------------------------------------------------------
diff --git a/syncope620/core/misc/src/test/java/org/apache/syncope/core/misc/security/PasswordGeneratorTest.java b/syncope620/core/misc/src/test/java/org/apache/syncope/core/misc/security/PasswordGeneratorTest.java
new file mode 100644
index 0000000..9c959b3
--- /dev/null
+++ b/syncope620/core/misc/src/test/java/org/apache/syncope/core/misc/security/PasswordGeneratorTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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 static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.syncope.common.lib.types.PasswordPolicySpec;
+import org.apache.syncope.core.misc.policy.InvalidPasswordPolicySpecException;
+import org.apache.syncope.core.misc.policy.PolicyPattern;
+import org.junit.Test;
+
+public class PasswordGeneratorTest {
+
+    private final PasswordGenerator passwordGenerator = new PasswordGenerator();
+
+    @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);
+        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);
+        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);
+        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);
+
+        PasswordPolicySpec passwordPolicySpec2 = createBasePasswordPolicySpec();
+        passwordPolicySpec.setMaxLength(10);
+
+        List<PasswordPolicySpec> passwordPolicySpecs = new ArrayList<>();
+        passwordPolicySpecs.add(passwordPolicySpec);
+        passwordPolicySpecs.add(passwordPolicySpec2);
+        passwordGenerator.generate(passwordPolicySpecs);
+    }
+
+    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;
+    }
+}