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/04/21 09:49:48 UTC

[33/40] syncope git commit: [SYNCOPE-119] New security model implemented

http://git-wip-us.apache.org/repos/asf/syncope/blob/65d652af/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
new file mode 100644
index 0000000..db49c0f
--- /dev/null
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/MappingUtils.java
@@ -0,0 +1,735 @@
+/*
+ * 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;
+
+import org.apache.syncope.core.misc.policy.InvalidPasswordPolicySpecException;
+import org.apache.syncope.core.misc.security.PasswordGenerator;
+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.Predicate;
+import org.apache.commons.jexl2.JexlContext;
+import org.apache.commons.jexl2.MapContext;
+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.CollectionUtils2;
+import org.apache.syncope.common.lib.mod.AttrMod;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
+import org.apache.syncope.common.lib.types.AttributableType;
+import org.apache.syncope.common.lib.types.IntMappingType;
+import org.apache.syncope.common.lib.types.MappingPurpose;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.VirSchemaDAO;
+import org.apache.syncope.core.persistence.api.entity.Attributable;
+import org.apache.syncope.core.persistence.api.entity.AttributableUtils;
+import org.apache.syncope.core.persistence.api.entity.AttributableUtilsFactory;
+import org.apache.syncope.core.persistence.api.entity.DerAttr;
+import org.apache.syncope.core.persistence.api.entity.EntityFactory;
+import org.apache.syncope.core.persistence.api.entity.ExternalResource;
+import org.apache.syncope.core.persistence.api.entity.MappingItem;
+import org.apache.syncope.core.persistence.api.entity.PlainAttr;
+import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
+import org.apache.syncope.core.persistence.api.entity.PlainSchema;
+import org.apache.syncope.core.persistence.api.entity.Subject;
+import org.apache.syncope.core.persistence.api.entity.VirAttr;
+import org.apache.syncope.core.persistence.api.entity.VirSchema;
+import org.apache.syncope.core.persistence.api.entity.membership.MDerSchema;
+import org.apache.syncope.core.persistence.api.entity.membership.MPlainSchema;
+import org.apache.syncope.core.persistence.api.entity.membership.MVirSchema;
+import org.apache.syncope.core.persistence.api.entity.membership.Membership;
+import org.apache.syncope.core.persistence.api.entity.group.GDerSchema;
+import org.apache.syncope.core.persistence.api.entity.group.GPlainAttrValue;
+import org.apache.syncope.core.persistence.api.entity.group.GPlainSchema;
+import org.apache.syncope.core.persistence.api.entity.group.GVirSchema;
+import org.apache.syncope.core.persistence.api.entity.group.Group;
+import org.apache.syncope.core.persistence.api.entity.user.UDerSchema;
+import org.apache.syncope.core.persistence.api.entity.user.UPlainAttrValue;
+import org.apache.syncope.core.persistence.api.entity.user.UPlainSchema;
+import org.apache.syncope.core.persistence.api.entity.user.UVirSchema;
+import org.apache.syncope.core.persistence.api.entity.user.User;
+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.identityconnectors.framework.common.FrameworkUtil;
+import org.identityconnectors.framework.common.objects.Attribute;
+import org.identityconnectors.framework.common.objects.AttributeBuilder;
+import org.identityconnectors.framework.common.objects.AttributeUtil;
+import org.identityconnectors.framework.common.objects.Name;
+import org.identityconnectors.framework.common.objects.OperationalAttributes;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.ConfigurableApplicationContext;
+
+public final class MappingUtils {
+
+    /**
+     * Logger.
+     */
+    private static final Logger LOG = LoggerFactory.getLogger(MappingUtils.class);
+
+    private static final Encryptor ENCRYPTOR = Encryptor.getInstance();
+
+    public static <T extends MappingItem> Collection<T> getMatchingMappingItems(
+            final Collection<T> items, final IntMappingType type) {
+
+        return CollectionUtils2.find(items, new Predicate<T>() {
+
+            @Override
+            public boolean evaluate(final T item) {
+                return item.getIntMappingType() == type;
+            }
+        });
+    }
+
+    public static <T extends MappingItem> Collection<T> getMatchingMappingItems(
+            final Collection<T> items, final String intAttrName, final IntMappingType type) {
+
+        return CollectionUtils2.find(items, new Predicate<T>() {
+
+            @Override
+            public boolean evaluate(final T item) {
+                return item.getIntMappingType() == type && intAttrName.equals(item.getIntAttrName());
+            }
+        });
+    }
+
+    public static <T extends MappingItem> Collection<T> getMatchingMappingItems(
+            final Collection<T> items, final String intAttrName) {
+
+        return CollectionUtils2.find(items, new Predicate<T>() {
+
+            @Override
+            public boolean evaluate(final T item) {
+                return intAttrName.equals(item.getIntAttrName());
+            }
+        });
+    }
+
+    /**
+     * Prepare attributes for sending to a connector instance.
+     *
+     * @param attrUtils user / group
+     * @param subject given user / group
+     * @param password clear-text password
+     * @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 membVAttrsToBeRemoved membership virtual attributes to be removed
+     * @param membVAttrsToBeUpdated membership virtual attributes to be added
+     * @param enable whether user must be enabled or not
+     * @param resource target resource
+     * @return account link + prepared attributes
+     */
+    public static Pair<String, Set<Attribute>> prepareAttributes(
+            final AttributableUtils attrUtils, final Subject<?, ?, ?> subject,
+            final String password,
+            final boolean changePwd,
+            final Set<String> vAttrsToBeRemoved,
+            final Map<String, AttrMod> vAttrsToBeUpdated,
+            final Set<String> membVAttrsToBeRemoved,
+            final Map<String, AttrMod> membVAttrsToBeUpdated,
+            final Boolean enable,
+            final ExternalResource resource) {
+
+        LOG.debug("Preparing resource attributes for {} on resource {} with attributes {}",
+                subject, resource, subject.getPlainAttrs());
+
+        final ConfigurableApplicationContext context = ApplicationContextProvider.getApplicationContext();
+        final VirAttrCache virAttrCache = context.getBean(VirAttrCache.class);
+        final PasswordGenerator passwordGenerator = context.getBean(PasswordGenerator.class);
+
+        Set<Attribute> attributes = new HashSet<>();
+        String accountId = null;
+
+        for (MappingItem mapping : attrUtils.getMappingItems(resource, MappingPurpose.PROPAGATION)) {
+            LOG.debug("Processing schema {}", mapping.getIntAttrName());
+
+            try {
+                if ((attrUtils.getType() == AttributableType.USER
+                        && mapping.getIntMappingType() == IntMappingType.UserVirtualSchema)
+                        || (attrUtils.getType() == AttributableType.GROUP
+                        && mapping.getIntMappingType() == IntMappingType.GroupVirtualSchema)) {
+
+                    LOG.debug("Expire entry cache {}-{}", subject.getKey(), mapping.getIntAttrName());
+                    virAttrCache.expire(attrUtils.getType(), subject.getKey(), mapping.getIntAttrName());
+                }
+
+                // SYNCOPE-458 expire cache also for membership virtual schemas
+                if (attrUtils.getType() == AttributableType.USER && mapping.getIntMappingType()
+                        == IntMappingType.MembershipVirtualSchema && (subject instanceof User)) {
+
+                    final User user = (User) subject;
+                    for (Membership membership : user.getMemberships()) {
+                        LOG.debug("Expire entry cache {}-{} for membership {}", subject.getKey(),
+                                mapping.getIntAttrName(), membership);
+                        virAttrCache.expire(AttributableType.MEMBERSHIP, membership.getKey(),
+                                mapping.getIntAttrName());
+                    }
+                }
+
+                Pair<String, Attribute> preparedAttr = prepareAttr(
+                        resource, mapping, subject, password, passwordGenerator, vAttrsToBeRemoved, vAttrsToBeUpdated,
+                        membVAttrsToBeRemoved, membVAttrsToBeUpdated);
+
+                if (preparedAttr != null && preparedAttr.getKey() != null) {
+                    accountId = preparedAttr.getKey();
+                }
+
+                if (preparedAttr != null && preparedAttr.getValue() != null) {
+                    Attribute alreadyAdded = AttributeUtil.find(preparedAttr.getValue().getName(), attributes);
+
+                    if (alreadyAdded == null) {
+                        attributes.add(preparedAttr.getValue());
+                    } else {
+                        attributes.remove(alreadyAdded);
+
+                        Set<Object> values = new HashSet<>(alreadyAdded.getValue());
+                        values.addAll(preparedAttr.getValue().getValue());
+
+                        attributes.add(AttributeBuilder.build(preparedAttr.getValue().getName(), values));
+                    }
+                }
+            } catch (Exception e) {
+                LOG.debug("Attribute '{}' processing failed", mapping.getIntAttrName(), e);
+            }
+        }
+
+        final Attribute accountIdExtAttr =
+                AttributeUtil.find(attrUtils.getAccountIdItem(resource).getExtAttrName(), attributes);
+        if (accountIdExtAttr != null) {
+            attributes.remove(accountIdExtAttr);
+            attributes.add(AttributeBuilder.build(attrUtils.getAccountIdItem(resource).getExtAttrName(), accountId));
+        }
+        attributes.add(MappingUtils.evaluateNAME(subject, resource, accountId));
+
+        if (enable != null) {
+            attributes.add(AttributeBuilder.buildEnabled(enable));
+        }
+        if (!changePwd) {
+            Attribute pwdAttr = AttributeUtil.find(OperationalAttributes.PASSWORD_NAME, attributes);
+            if (pwdAttr != null) {
+                attributes.remove(pwdAttr);
+            }
+        }
+
+        return new ImmutablePair<>(accountId, attributes);
+    }
+
+    /**
+     * Prepare an attribute to be sent to a connector instance.
+     *
+     * @param resource target resource
+     * @param mapItem mapping item for the given attribute
+     * @param subject given user
+     * @param password clear-text password
+     * @param passwordGenerator password generator
+     * @param vAttrsToBeRemoved virtual attributes to be removed
+     * @param vAttrsToBeUpdated virtual attributes to be added
+     * @return account link + prepared attribute
+     */
+    @SuppressWarnings("unchecked")
+    private static Pair<String, Attribute> prepareAttr(
+            final ExternalResource resource, final MappingItem mapItem,
+            final Subject<?, ?, ?> subject, final String password, final PasswordGenerator passwordGenerator,
+            final Set<String> vAttrsToBeRemoved, final Map<String, AttrMod> vAttrsToBeUpdated,
+            final Set<String> membVAttrsToBeRemoved, final Map<String, AttrMod> membVAttrsToBeUpdated) {
+
+        List<Attributable<?, ?, ?>> attributables = new ArrayList<>();
+
+        ConfigurableApplicationContext context = ApplicationContextProvider.getApplicationContext();
+        AttributableUtilsFactory attrUtilsFactory = context.getBean(AttributableUtilsFactory.class);
+        ConnObjectUtils connObjectUtils = context.getBean(ConnObjectUtils.class);
+
+        switch (mapItem.getIntMappingType().getAttributableType()) {
+            case USER:
+                if (subject instanceof User) {
+                    attributables.add(subject);
+                }
+                break;
+
+            case GROUP:
+                if (subject instanceof User) {
+                    for (Group group : ((User) subject).getGroups()) {
+                        connObjectUtils.retrieveVirAttrValues(group, attrUtilsFactory.getInstance(group));
+                        attributables.add(group);
+                    }
+                }
+                if (subject instanceof Group) {
+                    attributables.add(subject);
+                }
+                break;
+
+            case MEMBERSHIP:
+                if (subject instanceof User) {
+                    attributables.addAll(((User) subject).getMemberships());
+                }
+                break;
+
+            default:
+        }
+
+        List<PlainAttrValue> values = getIntValues(
+                resource, mapItem, attributables, vAttrsToBeRemoved, vAttrsToBeUpdated, membVAttrsToBeRemoved,
+                membVAttrsToBeUpdated);
+
+        PlainSchema schema = null;
+        boolean readOnlyVirSchema = false;
+        AttrSchemaType schemaType;
+        final Pair<String, Attribute> result;
+
+        switch (mapItem.getIntMappingType()) {
+            case UserPlainSchema:
+            case GroupPlainSchema:
+            case MembershipPlainSchema:
+                final PlainSchemaDAO plainSchemaDAO = context.getBean(PlainSchemaDAO.class);
+                schema = plainSchemaDAO.find(mapItem.getIntAttrName(),
+                        MappingUtils.getIntMappingTypeClass(mapItem.getIntMappingType()));
+                schemaType = schema == null ? AttrSchemaType.String : schema.getType();
+                break;
+
+            case UserVirtualSchema:
+            case GroupVirtualSchema:
+            case MembershipVirtualSchema:
+                VirSchemaDAO virSchemaDAO = context.getBean(VirSchemaDAO.class);
+                VirSchema virSchema = virSchemaDAO.find(mapItem.getIntAttrName(),
+                        MappingUtils.getIntMappingTypeClass(mapItem.getIntMappingType()));
+                readOnlyVirSchema = (virSchema != null && virSchema.isReadonly());
+                schemaType = AttrSchemaType.String;
+                break;
+
+            default:
+                schemaType = AttrSchemaType.String;
+        }
+
+        final String extAttrName = mapItem.getExtAttrName();
+
+        LOG.debug("Define mapping for: "
+                + "\n* ExtAttrName " + extAttrName
+                + "\n* is accountId " + mapItem.isAccountid()
+                + "\n* is password " + (mapItem.isPassword() || mapItem.getIntMappingType() == IntMappingType.Password)
+                + "\n* mandatory condition " + mapItem.getMandatoryCondition()
+                + "\n* Schema " + mapItem.getIntAttrName()
+                + "\n* IntMappingType " + mapItem.getIntMappingType().toString()
+                + "\n* ClassType " + schemaType.getType().getName()
+                + "\n* Values " + values);
+
+        if (readOnlyVirSchema) {
+            result = null;
+        } else {
+            final List<Object> objValues = new ArrayList<>();
+
+            for (PlainAttrValue value : values) {
+                if (FrameworkUtil.isSupportedAttributeType(schemaType.getType())) {
+                    objValues.add(value.getValue());
+                } else {
+                    objValues.add(value.getValueAsString());
+                }
+            }
+
+            if (mapItem.isAccountid()) {
+                result = new ImmutablePair<>(objValues.iterator().next().toString(), null);
+            } else if (mapItem.isPassword() && subject instanceof User) {
+                String passwordAttrValue = password;
+                if (StringUtils.isBlank(passwordAttrValue)) {
+                    User user = (User) subject;
+                    if (user.canDecodePassword()) {
+                        try {
+                            passwordAttrValue = ENCRYPTOR.decode(user.getPassword(), user.getCipherAlgorithm());
+                        } catch (Exception e) {
+                            LOG.error("Could not decode password for {}", user, e);
+                        }
+                    } else if (resource.isRandomPwdIfNotProvided()) {
+                        try {
+                            passwordAttrValue = passwordGenerator.generate(user);
+                        } catch (InvalidPasswordPolicySpecException e) {
+                            LOG.error("Could not generate policy-compliant random password for {}", user, e);
+                        }
+                    }
+                }
+
+                if (passwordAttrValue == null) {
+                    result = null;
+                } else {
+                    result = new ImmutablePair<>(
+                            null,
+                            AttributeBuilder.buildPassword(passwordAttrValue.toCharArray()));
+                }
+            } else {
+                if ((schema != null && schema.isMultivalue()) || attrUtilsFactory.getInstance(subject).getType()
+                        != mapItem.getIntMappingType().getAttributableType()) {
+
+                    result = new ImmutablePair<>(
+                            null,
+                            AttributeBuilder.build(extAttrName, objValues));
+                } else {
+                    result = new ImmutablePair<>(
+                            null, objValues.isEmpty()
+                                    ? AttributeBuilder.build(extAttrName)
+                                    : AttributeBuilder.build(extAttrName, objValues.iterator().next()));
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Build __NAME__ for propagation. First look if there ia a defined accountLink for the given resource (and in this
+     * case evaluate as JEXL); otherwise, take given accountId.
+     *
+     * @param subject given user / group
+     * @param resource target resource
+     * @param accountId accountId
+     * @return the value to be propagated as __NAME__
+     */
+    public static Name evaluateNAME(final Subject<?, ?, ?> subject,
+            final ExternalResource resource, final String accountId) {
+
+        final AttributableUtilsFactory attrUtilsFactory =
+                ApplicationContextProvider.getApplicationContext().getBean(AttributableUtilsFactory.class);
+        final AttributableUtils attrUtils = attrUtilsFactory.getInstance(subject);
+
+        if (StringUtils.isBlank(accountId)) {
+            // LOG error but avoid to throw exception: leave it to the external resource
+            LOG.error("Missing accountId for '{}': ", resource.getKey());
+        }
+
+        // Evaluate AccountLink expression
+        String evalAccountLink = null;
+        if (StringUtils.isNotBlank(attrUtils.getAccountLink(resource))) {
+            final JexlContext jexlContext = new MapContext();
+            JexlUtils.addFieldsToContext(subject, jexlContext);
+            JexlUtils.addAttrsToContext(subject.getPlainAttrs(), jexlContext);
+            JexlUtils.addDerAttrsToContext(subject.getDerAttrs(), subject.getPlainAttrs(), jexlContext);
+            evalAccountLink = JexlUtils.evaluate(attrUtils.getAccountLink(resource), jexlContext);
+        }
+
+        // If AccountLink evaluates to an empty string, just use the provided AccountId as Name(),
+        // otherwise evaluated AccountLink expression is taken as Name().
+        Name name;
+        if (StringUtils.isBlank(evalAccountLink)) {
+            // add AccountId as __NAME__ attribute ...
+            LOG.debug("Add AccountId [{}] as __NAME__", accountId);
+            name = new Name(accountId);
+        } else {
+            LOG.debug("Add AccountLink [{}] as __NAME__", evalAccountLink);
+            name = new Name(evalAccountLink);
+
+            // AccountId not propagated: it will be used to set the value for __UID__ attribute
+            LOG.debug("AccountId will be used just as __UID__ attribute");
+        }
+
+        return name;
+    }
+
+    private static String getGroupOwnerValue(
+            final ExternalResource resource, final Subject<?, ?, ?> subject) {
+
+        AttributableUtilsFactory attrUtilsFactory =
+                ApplicationContextProvider.getApplicationContext().getBean(AttributableUtilsFactory.class);
+
+        Pair<String, Attribute> preparedAttr = prepareAttr(
+                resource, attrUtilsFactory.getInstance(subject).getAccountIdItem(resource), subject, null, null,
+                Collections.<String>emptySet(), Collections.<String, AttrMod>emptyMap(),
+                Collections.<String>emptySet(), Collections.<String, AttrMod>emptyMap());
+        String accountId = preparedAttr.getKey();
+
+        final Name groupOwnerName = evaluateNAME(subject, resource, accountId);
+        return groupOwnerName.getNameValue();
+    }
+
+    /**
+     * Get attribute values.
+     *
+     * @param resource target resource
+     * @param mappingItem mapping item
+     * @param attributables list of attributables
+     * @param vAttrsToBeRemoved virtual attributes to be removed
+     * @param vAttrsToBeUpdated virtual attributes to be added
+     * @param membVAttrsToBeRemoved membership virtual attributes to be removed
+     * @param membVAttrsToBeUpdated membership virtual attributes to be added
+     * @return attribute values.
+     */
+    public static List<PlainAttrValue> getIntValues(final ExternalResource resource,
+            final MappingItem mappingItem, final List<Attributable<?, ?, ?>> attributables,
+            final Set<String> vAttrsToBeRemoved, final Map<String, AttrMod> vAttrsToBeUpdated,
+            final Set<String> membVAttrsToBeRemoved, final Map<String, AttrMod> membVAttrsToBeUpdated) {
+
+        LOG.debug("Get attributes for '{}' and mapping type '{}'", attributables, mappingItem.getIntMappingType());
+
+        final EntityFactory entityFactory =
+                ApplicationContextProvider.getApplicationContext().getBean(EntityFactory.class);
+        List<PlainAttrValue> values = new ArrayList<>();
+        PlainAttrValue attrValue;
+        switch (mappingItem.getIntMappingType()) {
+            case UserPlainSchema:
+            case GroupPlainSchema:
+            case MembershipPlainSchema:
+                for (Attributable<?, ?, ?> attributable : attributables) {
+                    final PlainAttr attr = attributable.getPlainAttr(mappingItem.getIntAttrName());
+                    if (attr != null) {
+                        if (attr.getUniqueValue() != null) {
+                            values.add(attr.getUniqueValue());
+                        } else if (attr.getValues() != null) {
+                            values.addAll(attr.getValues());
+                        }
+                    }
+
+                    LOG.debug("Retrieved attribute {}"
+                            + "\n* IntAttrName {}"
+                            + "\n* IntMappingType {}"
+                            + "\n* Attribute values {}",
+                            attr, mappingItem.getIntAttrName(), mappingItem.getIntMappingType(), values);
+                }
+
+                break;
+
+            case UserVirtualSchema:
+            case GroupVirtualSchema:
+                for (Attributable<?, ?, ?> attributable : attributables) {
+                    VirAttr virAttr = attributable.getVirAttr(mappingItem.getIntAttrName());
+                    if (virAttr != null) {
+                        if (vAttrsToBeRemoved != null && vAttrsToBeUpdated != null) {
+                            if (vAttrsToBeUpdated.containsKey(mappingItem.getIntAttrName())) {
+                                virAttr.getValues().clear();
+                                virAttr.getValues().addAll(
+                                        vAttrsToBeUpdated.get(mappingItem.getIntAttrName()).getValuesToBeAdded());
+                            } else if (vAttrsToBeRemoved.contains(mappingItem.getIntAttrName())) {
+                                virAttr.getValues().clear();
+                            } else {
+                                throw new IllegalArgumentException("Don't need to update virtual attribute '"
+                                        + mappingItem.getIntAttrName() + "'");
+                            }
+                        }
+                        if (virAttr.getValues() != null) {
+                            for (String value : virAttr.getValues()) {
+                                attrValue = entityFactory.newEntity(UPlainAttrValue.class);
+                                attrValue.setStringValue(value);
+                                values.add(attrValue);
+                            }
+                        }
+                    }
+
+                    LOG.debug("Retrieved {} virtual attribute {}"
+                            + "\n* IntAttrName {}"
+                            + "\n* IntMappingType {}"
+                            + "\n* Attribute values {}",
+                            attributable.getClass().getSimpleName(),
+                            virAttr, mappingItem.getIntAttrName(), mappingItem.getIntMappingType(), values);
+                }
+                break;
+
+            case MembershipVirtualSchema:
+                for (Attributable<?, ?, ?> attributable : attributables) {
+                    VirAttr virAttr = attributable.getVirAttr(mappingItem.getIntAttrName());
+                    if (virAttr != null) {
+                        if (membVAttrsToBeRemoved != null && membVAttrsToBeUpdated != null) {
+                            if (membVAttrsToBeUpdated.containsKey(mappingItem.getIntAttrName())) {
+                                virAttr.getValues().clear();
+                                virAttr.getValues().addAll(
+                                        membVAttrsToBeUpdated.get(mappingItem.getIntAttrName()).getValuesToBeAdded());
+                            } else if (membVAttrsToBeRemoved.contains(mappingItem.getIntAttrName())) {
+                                virAttr.getValues().clear();
+                            } else {
+                                throw new IllegalArgumentException("Don't need to update membership virtual attribute '"
+                                        + mappingItem.getIntAttrName() + "'");
+                            }
+                        }
+                        if (virAttr.getValues() != null) {
+                            for (String value : virAttr.getValues()) {
+                                attrValue = entityFactory.newEntity(UPlainAttrValue.class);
+                                attrValue.setStringValue(value);
+                                values.add(attrValue);
+                            }
+                        }
+                    }
+
+                    LOG.debug("Retrieved {} virtual attribute {}"
+                            + "\n* IntAttrName {}"
+                            + "\n* IntMappingType {}"
+                            + "\n* Attribute values {}",
+                            attributable.getClass().getSimpleName(),
+                            virAttr, mappingItem.getIntAttrName(), mappingItem.getIntMappingType(), values);
+                }
+                break;
+
+            case UserDerivedSchema:
+            case GroupDerivedSchema:
+            case MembershipDerivedSchema:
+                for (Attributable<?, ?, ?> attributable : attributables) {
+                    DerAttr derAttr = attributable.getDerAttr(mappingItem.getIntAttrName());
+                    if (derAttr != null) {
+                        attrValue = attributable instanceof Group
+                                ? entityFactory.newEntity(GPlainAttrValue.class)
+                                : entityFactory.newEntity(UPlainAttrValue.class);
+                        attrValue.setStringValue(derAttr.getValue(attributable.getPlainAttrs()));
+                        values.add(attrValue);
+                    }
+
+                    LOG.debug("Retrieved attribute {}"
+                            + "\n* IntAttrName {}"
+                            + "\n* IntMappingType {}"
+                            + "\n* Attribute values {}",
+                            derAttr, mappingItem.getIntAttrName(), mappingItem.getIntMappingType(), values);
+                }
+                break;
+
+            case UserId:
+            case GroupId:
+            case MembershipId:
+                for (Attributable<?, ?, ?> attributable : attributables) {
+                    attrValue = entityFactory.newEntity(UPlainAttrValue.class);
+                    attrValue.setStringValue(attributable.getKey().toString());
+                    values.add(attrValue);
+                }
+                break;
+
+            case Username:
+                for (Attributable<?, ?, ?> attributable : attributables) {
+                    if (attributable instanceof User) {
+                        attrValue = entityFactory.newEntity(UPlainAttrValue.class);
+                        attrValue.setStringValue(((User) attributable).getUsername());
+                        values.add(attrValue);
+                    }
+                }
+                break;
+
+            case GroupName:
+                for (Attributable<?, ?, ?> attributable : attributables) {
+                    if (attributable instanceof Group) {
+                        attrValue = entityFactory.newEntity(GPlainAttrValue.class);
+                        attrValue.setStringValue(((Group) attributable).getName());
+                        values.add(attrValue);
+                    }
+                }
+                break;
+
+            case GroupOwnerSchema:
+                for (Attributable<?, ?, ?> attributable : attributables) {
+                    if (attributable instanceof Group) {
+                        Group group = (Group) attributable;
+                        String groupOwnerValue = null;
+                        if (group.getUserOwner() != null && resource.getUmapping() != null) {
+                            groupOwnerValue = getGroupOwnerValue(resource, group.getUserOwner());
+                        }
+                        if (group.getGroupOwner() != null && resource.getGmapping() != null) {
+                            groupOwnerValue = getGroupOwnerValue(resource, group.getGroupOwner());
+                        }
+
+                        if (StringUtils.isNotBlank(groupOwnerValue)) {
+                            attrValue = entityFactory.newEntity(GPlainAttrValue.class);
+                            attrValue.setStringValue(groupOwnerValue);
+                            values.add(attrValue);
+                        }
+                    }
+                }
+                break;
+
+            default:
+        }
+
+        LOG.debug("Retrieved values '{}'", values);
+
+        return values;
+    }
+
+    /**
+     * Get accountId internal value.
+     *
+     * @param attributable attributable
+     * @param accountIdItem accountId mapping item
+     * @param resource external resource
+     * @return accountId internal value
+     */
+    public static String getAccountIdValue(final Attributable<?, ?, ?> attributable,
+            final ExternalResource resource, final MappingItem accountIdItem) {
+
+        List<PlainAttrValue> values = getIntValues(resource, accountIdItem,
+                Collections.<Attributable<?, ?, ?>>singletonList(attributable), null, null, null, null);
+        return values == null || values.isEmpty()
+                ? null
+                : values.get(0).getValueAsString();
+    }
+
+    /**
+     * For given source mapping type, return the corresponding Class object.
+     *
+     * @param intMappingType source mapping type
+     * @return corresponding Class object, if any (can be null)
+     */
+    @SuppressWarnings("rawtypes")
+    public static Class getIntMappingTypeClass(final IntMappingType intMappingType) {
+        Class result;
+
+        switch (intMappingType) {
+            case UserPlainSchema:
+                result = UPlainSchema.class;
+                break;
+
+            case GroupPlainSchema:
+                result = GPlainSchema.class;
+                break;
+
+            case MembershipPlainSchema:
+                result = MPlainSchema.class;
+                break;
+
+            case UserDerivedSchema:
+                result = UDerSchema.class;
+                break;
+
+            case GroupDerivedSchema:
+                result = GDerSchema.class;
+                break;
+
+            case MembershipDerivedSchema:
+                result = MDerSchema.class;
+                break;
+
+            case UserVirtualSchema:
+                result = UVirSchema.class;
+                break;
+
+            case GroupVirtualSchema:
+                result = GVirSchema.class;
+                break;
+
+            case MembershipVirtualSchema:
+                result = MVirSchema.class;
+                break;
+
+            default:
+                result = null;
+        }
+
+        return result;
+    }
+
+    /**
+     * Private default constructor, for static-only classes.
+     */
+    private MappingUtils() {
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/65d652af/core/misc/src/main/java/org/apache/syncope/core/misc/RealmUtils.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/RealmUtils.java b/core/misc/src/main/java/org/apache/syncope/core/misc/RealmUtils.java
new file mode 100644
index 0000000..7a921ee
--- /dev/null
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/RealmUtils.java
@@ -0,0 +1,61 @@
+/*
+ * 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;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+public final class RealmUtils {
+
+    public static String getGroupOwnerRealm(final String realmPath, final Long groupId) {
+        return realmPath + "@" + groupId;
+    }
+
+    public static boolean normalizingAddTo(final Set<String> realms, final String newRealm) {
+        boolean dontAdd = false;
+        Set<String> toRemove = new HashSet<>();
+        for (String realm : realms) {
+            if (newRealm.startsWith(realm)) {
+                dontAdd = true;
+            } else if (realm.startsWith(newRealm)) {
+                toRemove.add(realm);
+            }
+        }
+
+        realms.removeAll(toRemove);
+        if (!dontAdd) {
+            realms.add(newRealm);
+        }
+        return !dontAdd;
+    }
+
+    public static Set<String> normalize(final Collection<String> realms) {
+        Set<String> normalized = new HashSet<>();
+        for (String realm : realms) {
+            normalizingAddTo(normalized, realm);
+        }
+
+        return normalized;
+    }
+
+    private RealmUtils() {
+        // empty constructor for static utility class 
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/65d652af/core/misc/src/main/java/org/apache/syncope/core/misc/jexl/JexlUtil.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/jexl/JexlUtil.java b/core/misc/src/main/java/org/apache/syncope/core/misc/jexl/JexlUtil.java
deleted file mode 100644
index b624eaa..0000000
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/jexl/JexlUtil.java
+++ /dev/null
@@ -1,285 +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.jexl;
-
-import java.beans.IntrospectionException;
-import java.beans.Introspector;
-import java.beans.PropertyDescriptor;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-import org.apache.commons.jexl2.Expression;
-import org.apache.commons.jexl2.JexlContext;
-import org.apache.commons.jexl2.JexlEngine;
-import org.apache.commons.jexl2.JexlException;
-import org.apache.commons.jexl2.MapContext;
-import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.syncope.common.lib.to.AbstractAttributableTO;
-import org.apache.syncope.common.lib.to.AttrTO;
-import org.apache.syncope.core.persistence.api.entity.Attributable;
-import org.apache.syncope.core.persistence.api.entity.DerAttr;
-import org.apache.syncope.core.persistence.api.entity.PlainAttr;
-import org.apache.syncope.core.persistence.api.entity.VirAttr;
-import org.apache.syncope.core.misc.DataFormat;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * JEXL <a href="http://commons.apache.org/jexl/reference/index.html">reference</a> is available.
- */
-public final class JexlUtil {
-
-    private static final Logger LOG = LoggerFactory.getLogger(JexlUtil.class);
-
-    private static final String[] IGNORE_FIELDS = { "password", "clearPassword", "serialVersionUID", "class" };
-
-    private static JexlEngine JEXL_ENGINE;
-
-    private static JexlEngine getEngine() {
-        synchronized (LOG) {
-            if (JEXL_ENGINE == null) {
-                JEXL_ENGINE = new JexlEngine(new ClassFreeUberspectImpl(null), null, null, null);
-                JEXL_ENGINE.setClassLoader(new EmptyClassLoader());
-                JEXL_ENGINE.setCache(512);
-                JEXL_ENGINE.setLenient(true);
-                JEXL_ENGINE.setSilent(false);
-            }
-        }
-
-        return JEXL_ENGINE;
-    }
-
-    public static boolean isExpressionValid(final String expression) {
-        boolean result;
-        try {
-            getEngine().createExpression(expression);
-            result = true;
-        } catch (JexlException e) {
-            LOG.error("Invalid jexl expression: " + expression, e);
-            result = false;
-        }
-
-        return result;
-    }
-
-    public static String evaluate(final String expression, final JexlContext jexlContext) {
-        String result = StringUtils.EMPTY;
-
-        if (StringUtils.isNotBlank(expression) && jexlContext != null) {
-            try {
-                Expression jexlExpression = getEngine().createExpression(expression);
-                Object evaluated = jexlExpression.evaluate(jexlContext);
-                if (evaluated != null) {
-                    result = evaluated.toString();
-                }
-            } catch (Exception e) {
-                LOG.error("Error while evaluating JEXL expression: " + expression, e);
-            }
-        } else {
-            LOG.debug("Expression not provided or invalid context");
-        }
-
-        return result;
-    }
-
-    public static JexlContext addFieldsToContext(final Object object, final JexlContext jexlContext) {
-        JexlContext context = jexlContext == null ? new MapContext() : jexlContext;
-
-        try {
-            for (PropertyDescriptor desc : Introspector.getBeanInfo(object.getClass()).getPropertyDescriptors()) {
-                final Class<?> type = desc.getPropertyType();
-                final String fieldName = desc.getName();
-
-                if ((!fieldName.startsWith("pc"))
-                        && (!ArrayUtils.contains(IGNORE_FIELDS, fieldName))
-                        && (!Iterable.class.isAssignableFrom(type))
-                        && (!type.isArray())) {
-                    try {
-                        final Method getter = desc.getReadMethod();
-
-                        final Object fieldValue;
-
-                        if (getter == null) {
-                            final Field field = object.getClass().getDeclaredField(fieldName);
-                            field.setAccessible(true);
-                            fieldValue = field.get(object);
-                        } else {
-                            fieldValue = getter.invoke(object);
-                        }
-
-                        context.set(fieldName, fieldValue == null
-                                ? StringUtils.EMPTY
-                                : (type.equals(Date.class)
-                                        ? DataFormat.format((Date) fieldValue, false)
-                                        : fieldValue));
-
-                        LOG.debug("Add field {} with value {}", fieldName, fieldValue);
-
-                    } catch (Exception iae) {
-                        LOG.error("Reading '{}' value error", fieldName, iae);
-                    }
-                }
-            }
-        } catch (IntrospectionException ie) {
-            LOG.error("Reading class attributes error", ie);
-        }
-
-        return context;
-    }
-
-    public static JexlContext addAttrsToContext(final Collection<? extends PlainAttr> attrs,
-            final JexlContext jexlContext) {
-
-        JexlContext context = jexlContext == null
-                ? new MapContext()
-                : jexlContext;
-
-        for (PlainAttr attr : attrs) {
-            if (attr.getSchema() != null) {
-                List<String> attrValues = attr.getValuesAsStrings();
-                String expressionValue = attrValues.isEmpty()
-                        ? StringUtils.EMPTY
-                        : attrValues.get(0);
-
-                LOG.debug("Add attribute {} with value {}", attr.getSchema().getKey(), expressionValue);
-
-                context.set(attr.getSchema().getKey(), expressionValue);
-            }
-        }
-
-        return context;
-    }
-
-    public static JexlContext addDerAttrsToContext(final Collection<? extends DerAttr> derAttrs,
-            final Collection<? extends PlainAttr> attrs, final JexlContext jexlContext) {
-
-        JexlContext context = jexlContext == null
-                ? new MapContext()
-                : jexlContext;
-
-        for (DerAttr derAttr : derAttrs) {
-            if (derAttr.getSchema() != null) {
-                String expressionValue = derAttr.getValue(attrs);
-                if (expressionValue == null) {
-                    expressionValue = StringUtils.EMPTY;
-                }
-
-                LOG.debug("Add derived attribute {} with value {}", derAttr.getSchema().getKey(), expressionValue);
-
-                context.set(derAttr.getSchema().getKey(), expressionValue);
-            }
-        }
-
-        return context;
-    }
-
-    public static JexlContext addVirAttrsToContext(final Collection<? extends VirAttr> virAttrs,
-            final JexlContext jexlContext) {
-
-        JexlContext context = jexlContext == null
-                ? new MapContext()
-                : jexlContext;
-
-        for (VirAttr virAttr : virAttrs) {
-            if (virAttr.getSchema() != null) {
-                List<String> attrValues = virAttr.getValues();
-                String expressionValue = attrValues.isEmpty()
-                        ? StringUtils.EMPTY
-                        : attrValues.get(0);
-
-                LOG.debug("Add virtual attribute {} with value {}", virAttr.getSchema().getKey(), expressionValue);
-
-                context.set(virAttr.getSchema().getKey(), expressionValue);
-            }
-        }
-
-        return context;
-    }
-
-    public static boolean evaluateMandatoryCondition(
-            final String mandatoryCondition, final Attributable<?, ?, ?> attributable) {
-
-        JexlContext jexlContext = new MapContext();
-        addAttrsToContext(attributable.getPlainAttrs(), jexlContext);
-        addDerAttrsToContext(attributable.getDerAttrs(), attributable.getPlainAttrs(), jexlContext);
-        addVirAttrsToContext(attributable.getVirAttrs(), jexlContext);
-
-        return Boolean.parseBoolean(evaluate(mandatoryCondition, jexlContext));
-    }
-
-    public static String evaluate(final String expression,
-            final Attributable<?, ?, ?> attributable, final Collection<? extends PlainAttr> attributes) {
-
-        final JexlContext jexlContext = new MapContext();
-        JexlUtil.addAttrsToContext(attributes, jexlContext);
-        JexlUtil.addFieldsToContext(attributable, jexlContext);
-
-        // Evaluate expression using the context prepared before
-        return evaluate(expression, jexlContext);
-    }
-
-    public static String evaluate(final String expression, final AbstractAttributableTO attributableTO) {
-        final JexlContext context = new MapContext();
-
-        addFieldsToContext(attributableTO, context);
-
-        for (AttrTO plainAttr : attributableTO.getPlainAttrs()) {
-            List<String> values = plainAttr.getValues();
-            String expressionValue = values.isEmpty()
-                    ? StringUtils.EMPTY
-                    : values.get(0);
-
-            LOG.debug("Add plain attribute {} with value {}", plainAttr.getSchema(), expressionValue);
-
-            context.set(plainAttr.getSchema(), expressionValue);
-        }
-        for (AttrTO derAttr : attributableTO.getDerAttrs()) {
-            List<String> values = derAttr.getValues();
-            String expressionValue = values.isEmpty()
-                    ? StringUtils.EMPTY
-                    : values.get(0);
-
-            LOG.debug("Add derived attribute {} with value {}", derAttr.getSchema(), expressionValue);
-
-            context.set(derAttr.getSchema(), expressionValue);
-        }
-        for (AttrTO virAttr : attributableTO.getVirAttrs()) {
-            List<String> values = virAttr.getValues();
-            String expressionValue = values.isEmpty()
-                    ? StringUtils.EMPTY
-                    : values.get(0);
-
-            LOG.debug("Add virtual attribute {} with value {}", virAttr.getSchema(), expressionValue);
-
-            context.set(virAttr.getSchema(), expressionValue);
-        }
-
-        // Evaluate expression using the context prepared before
-        return evaluate(expression, context);
-    }
-
-    /**
-     * Private default constructor, for static-only classes.
-     */
-    private JexlUtil() {
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/65d652af/core/misc/src/main/java/org/apache/syncope/core/misc/jexl/JexlUtils.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/jexl/JexlUtils.java b/core/misc/src/main/java/org/apache/syncope/core/misc/jexl/JexlUtils.java
new file mode 100644
index 0000000..bc3159c
--- /dev/null
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/jexl/JexlUtils.java
@@ -0,0 +1,285 @@
+/*
+ * 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.jexl;
+
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import org.apache.commons.jexl2.Expression;
+import org.apache.commons.jexl2.JexlContext;
+import org.apache.commons.jexl2.JexlEngine;
+import org.apache.commons.jexl2.JexlException;
+import org.apache.commons.jexl2.MapContext;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.to.AbstractAttributableTO;
+import org.apache.syncope.common.lib.to.AttrTO;
+import org.apache.syncope.core.persistence.api.entity.Attributable;
+import org.apache.syncope.core.persistence.api.entity.DerAttr;
+import org.apache.syncope.core.persistence.api.entity.PlainAttr;
+import org.apache.syncope.core.persistence.api.entity.VirAttr;
+import org.apache.syncope.core.misc.DataFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * JEXL <a href="http://commons.apache.org/jexl/reference/index.html">reference</a> is available.
+ */
+public final class JexlUtils {
+
+    private static final Logger LOG = LoggerFactory.getLogger(JexlUtils.class);
+
+    private static final String[] IGNORE_FIELDS = { "password", "clearPassword", "serialVersionUID", "class" };
+
+    private static JexlEngine JEXL_ENGINE;
+
+    private static JexlEngine getEngine() {
+        synchronized (LOG) {
+            if (JEXL_ENGINE == null) {
+                JEXL_ENGINE = new JexlEngine(new ClassFreeUberspectImpl(null), null, null, null);
+                JEXL_ENGINE.setClassLoader(new EmptyClassLoader());
+                JEXL_ENGINE.setCache(512);
+                JEXL_ENGINE.setLenient(true);
+                JEXL_ENGINE.setSilent(false);
+            }
+        }
+
+        return JEXL_ENGINE;
+    }
+
+    public static boolean isExpressionValid(final String expression) {
+        boolean result;
+        try {
+            getEngine().createExpression(expression);
+            result = true;
+        } catch (JexlException e) {
+            LOG.error("Invalid jexl expression: " + expression, e);
+            result = false;
+        }
+
+        return result;
+    }
+
+    public static String evaluate(final String expression, final JexlContext jexlContext) {
+        String result = StringUtils.EMPTY;
+
+        if (StringUtils.isNotBlank(expression) && jexlContext != null) {
+            try {
+                Expression jexlExpression = getEngine().createExpression(expression);
+                Object evaluated = jexlExpression.evaluate(jexlContext);
+                if (evaluated != null) {
+                    result = evaluated.toString();
+                }
+            } catch (Exception e) {
+                LOG.error("Error while evaluating JEXL expression: " + expression, e);
+            }
+        } else {
+            LOG.debug("Expression not provided or invalid context");
+        }
+
+        return result;
+    }
+
+    public static JexlContext addFieldsToContext(final Object object, final JexlContext jexlContext) {
+        JexlContext context = jexlContext == null ? new MapContext() : jexlContext;
+
+        try {
+            for (PropertyDescriptor desc : Introspector.getBeanInfo(object.getClass()).getPropertyDescriptors()) {
+                final Class<?> type = desc.getPropertyType();
+                final String fieldName = desc.getName();
+
+                if ((!fieldName.startsWith("pc"))
+                        && (!ArrayUtils.contains(IGNORE_FIELDS, fieldName))
+                        && (!Iterable.class.isAssignableFrom(type))
+                        && (!type.isArray())) {
+                    try {
+                        final Method getter = desc.getReadMethod();
+
+                        final Object fieldValue;
+
+                        if (getter == null) {
+                            final Field field = object.getClass().getDeclaredField(fieldName);
+                            field.setAccessible(true);
+                            fieldValue = field.get(object);
+                        } else {
+                            fieldValue = getter.invoke(object);
+                        }
+
+                        context.set(fieldName, fieldValue == null
+                                ? StringUtils.EMPTY
+                                : (type.equals(Date.class)
+                                        ? DataFormat.format((Date) fieldValue, false)
+                                        : fieldValue));
+
+                        LOG.debug("Add field {} with value {}", fieldName, fieldValue);
+
+                    } catch (Exception iae) {
+                        LOG.error("Reading '{}' value error", fieldName, iae);
+                    }
+                }
+            }
+        } catch (IntrospectionException ie) {
+            LOG.error("Reading class attributes error", ie);
+        }
+
+        return context;
+    }
+
+    public static JexlContext addAttrsToContext(final Collection<? extends PlainAttr> attrs,
+            final JexlContext jexlContext) {
+
+        JexlContext context = jexlContext == null
+                ? new MapContext()
+                : jexlContext;
+
+        for (PlainAttr attr : attrs) {
+            if (attr.getSchema() != null) {
+                List<String> attrValues = attr.getValuesAsStrings();
+                String expressionValue = attrValues.isEmpty()
+                        ? StringUtils.EMPTY
+                        : attrValues.get(0);
+
+                LOG.debug("Add attribute {} with value {}", attr.getSchema().getKey(), expressionValue);
+
+                context.set(attr.getSchema().getKey(), expressionValue);
+            }
+        }
+
+        return context;
+    }
+
+    public static JexlContext addDerAttrsToContext(final Collection<? extends DerAttr> derAttrs,
+            final Collection<? extends PlainAttr> attrs, final JexlContext jexlContext) {
+
+        JexlContext context = jexlContext == null
+                ? new MapContext()
+                : jexlContext;
+
+        for (DerAttr derAttr : derAttrs) {
+            if (derAttr.getSchema() != null) {
+                String expressionValue = derAttr.getValue(attrs);
+                if (expressionValue == null) {
+                    expressionValue = StringUtils.EMPTY;
+                }
+
+                LOG.debug("Add derived attribute {} with value {}", derAttr.getSchema().getKey(), expressionValue);
+
+                context.set(derAttr.getSchema().getKey(), expressionValue);
+            }
+        }
+
+        return context;
+    }
+
+    public static JexlContext addVirAttrsToContext(final Collection<? extends VirAttr> virAttrs,
+            final JexlContext jexlContext) {
+
+        JexlContext context = jexlContext == null
+                ? new MapContext()
+                : jexlContext;
+
+        for (VirAttr virAttr : virAttrs) {
+            if (virAttr.getSchema() != null) {
+                List<String> attrValues = virAttr.getValues();
+                String expressionValue = attrValues.isEmpty()
+                        ? StringUtils.EMPTY
+                        : attrValues.get(0);
+
+                LOG.debug("Add virtual attribute {} with value {}", virAttr.getSchema().getKey(), expressionValue);
+
+                context.set(virAttr.getSchema().getKey(), expressionValue);
+            }
+        }
+
+        return context;
+    }
+
+    public static boolean evaluateMandatoryCondition(
+            final String mandatoryCondition, final Attributable<?, ?, ?> attributable) {
+
+        JexlContext jexlContext = new MapContext();
+        addAttrsToContext(attributable.getPlainAttrs(), jexlContext);
+        addDerAttrsToContext(attributable.getDerAttrs(), attributable.getPlainAttrs(), jexlContext);
+        addVirAttrsToContext(attributable.getVirAttrs(), jexlContext);
+
+        return Boolean.parseBoolean(evaluate(mandatoryCondition, jexlContext));
+    }
+
+    public static String evaluate(final String expression,
+            final Attributable<?, ?, ?> attributable, final Collection<? extends PlainAttr> attributes) {
+
+        final JexlContext jexlContext = new MapContext();
+        JexlUtils.addAttrsToContext(attributes, jexlContext);
+        JexlUtils.addFieldsToContext(attributable, jexlContext);
+
+        // Evaluate expression using the context prepared before
+        return evaluate(expression, jexlContext);
+    }
+
+    public static String evaluate(final String expression, final AbstractAttributableTO attributableTO) {
+        final JexlContext context = new MapContext();
+
+        addFieldsToContext(attributableTO, context);
+
+        for (AttrTO plainAttr : attributableTO.getPlainAttrs()) {
+            List<String> values = plainAttr.getValues();
+            String expressionValue = values.isEmpty()
+                    ? StringUtils.EMPTY
+                    : values.get(0);
+
+            LOG.debug("Add plain attribute {} with value {}", plainAttr.getSchema(), expressionValue);
+
+            context.set(plainAttr.getSchema(), expressionValue);
+        }
+        for (AttrTO derAttr : attributableTO.getDerAttrs()) {
+            List<String> values = derAttr.getValues();
+            String expressionValue = values.isEmpty()
+                    ? StringUtils.EMPTY
+                    : values.get(0);
+
+            LOG.debug("Add derived attribute {} with value {}", derAttr.getSchema(), expressionValue);
+
+            context.set(derAttr.getSchema(), expressionValue);
+        }
+        for (AttrTO virAttr : attributableTO.getVirAttrs()) {
+            List<String> values = virAttr.getValues();
+            String expressionValue = values.isEmpty()
+                    ? StringUtils.EMPTY
+                    : values.get(0);
+
+            LOG.debug("Add virtual attribute {} with value {}", virAttr.getSchema(), expressionValue);
+
+            context.set(virAttr.getSchema(), expressionValue);
+        }
+
+        // Evaluate expression using the context prepared before
+        return evaluate(expression, context);
+    }
+
+    /**
+     * Private default constructor, for static-only classes.
+     */
+    private JexlUtils() {
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/65d652af/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 ab0e7fb..4b8faec 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
@@ -91,8 +91,8 @@ public class AccountPolicyEnforcer implements PolicyEnforcer<AccountPolicySpec,
 
         // check for subsequent failed logins
         if (userSuspender != null
-                && user.getFailedLogins() != null && policy.getPermittedLoginRetries() > 0
-                && user.getFailedLogins() > policy.getPermittedLoginRetries() && !user.isSuspended()) {
+                && user.getFailedLogins() != null && policy.getMaxAuthenticationAttempts() > 0
+                && user.getFailedLogins() > policy.getMaxAuthenticationAttempts() && !user.isSuspended()) {
 
             userSuspender.suspend(user, policy.isPropagateSuspension());
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/65d652af/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 a3bd84e..b9cfb64 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
@@ -48,9 +48,8 @@ public class PolicyEvaluator {
         T result = null;
         switch (policy.getType()) {
             case PASSWORD:
-            case GLOBAL_PASSWORD:
-                final PasswordPolicySpec ppSpec = policy.getSpecification(PasswordPolicySpec.class);
-                final PasswordPolicySpec evaluatedPPSpec = new PasswordPolicySpec();
+                PasswordPolicySpec ppSpec = policy.getSpecification(PasswordPolicySpec.class);
+                PasswordPolicySpec evaluatedPPSpec = new PasswordPolicySpec();
 
                 BeanUtils.copyProperties(ppSpec, evaluatedPPSpec, new String[] { "schemasNotPermitted" });
 
@@ -79,7 +78,6 @@ public class PolicyEvaluator {
                 break;
 
             case ACCOUNT:
-            case GLOBAL_ACCOUNT:
                 final AccountPolicySpec spec = policy.getSpecification(AccountPolicySpec.class);
                 final AccountPolicySpec accountPolicy = new AccountPolicySpec();
 
@@ -99,7 +97,6 @@ public class PolicyEvaluator {
                 break;
 
             case SYNC:
-            case GLOBAL_SYNC:
             default:
                 result = null;
         }

http://git-wip-us.apache.org/repos/asf/syncope/blob/65d652af/core/misc/src/main/java/org/apache/syncope/core/misc/search/SearchCondVisitor.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/search/SearchCondVisitor.java b/core/misc/src/main/java/org/apache/syncope/core/misc/search/SearchCondVisitor.java
index 949dee7..1062a93 100644
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/search/SearchCondVisitor.java
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/search/SearchCondVisitor.java
@@ -31,7 +31,6 @@ import org.apache.syncope.common.lib.search.SpecialAttr;
 import org.apache.syncope.common.lib.to.GroupTO;
 import org.apache.syncope.common.lib.to.UserTO;
 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;
@@ -108,12 +107,6 @@ public class SearchCondVisitor extends AbstractSearchConditionVisitor<SearchBean
                             leaf = SearchCond.getLeafCond(resourceCond);
                             break;
 
-                        case ENTITLEMENTS:
-                            EntitlementCond entitlementCond = new EntitlementCond();
-                            entitlementCond.setExpression(value);
-                            leaf = SearchCond.getLeafCond(entitlementCond);
-                            break;
-
                         default:
                             throw new IllegalArgumentException(
                                     String.format("Special attr name %s is not supported", specialAttrName));

http://git-wip-us.apache.org/repos/asf/syncope/blob/65d652af/core/misc/src/main/java/org/apache/syncope/core/misc/security/AuthContextUtil.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/security/AuthContextUtil.java b/core/misc/src/main/java/org/apache/syncope/core/misc/security/AuthContextUtil.java
deleted file mode 100644
index d88c353..0000000
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/security/AuthContextUtil.java
+++ /dev/null
@@ -1,74 +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.security;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import org.apache.syncope.common.lib.SyncopeConstants;
-import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.GrantedAuthority;
-import org.springframework.security.core.authority.SimpleGrantedAuthority;
-import org.springframework.security.core.context.SecurityContext;
-import org.springframework.security.core.context.SecurityContextHolder;
-
-public final class AuthContextUtil {
-
-    public static String getAuthenticatedUsername() {
-        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
-        return authentication == null ? SyncopeConstants.UNAUTHENTICATED : authentication.getName();
-    }
-
-    public static Set<String> getOwnedEntitlementNames() {
-        final Set<String> result = new HashSet<>();
-
-        final SecurityContext ctx = SecurityContextHolder.getContext();
-
-        if (ctx != null && ctx.getAuthentication() != null && ctx.getAuthentication().getAuthorities() != null) {
-            for (GrantedAuthority authority : ctx.getAuthentication().getAuthorities()) {
-                result.add(authority.getAuthority());
-            }
-        }
-
-        return result;
-    }
-
-    /**
-     * Extend the current authentication context to include the given group.
-     *
-     * @param groupKey group key
-     * @param groupEntitlement group entitlement
-     */
-    public static void extendAuthContext(final Long groupKey, final String groupEntitlement) {
-        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
-        List<GrantedAuthority> authorities = new ArrayList<>(auth.getAuthorities());
-        authorities.add(new SimpleGrantedAuthority(groupEntitlement));
-        Authentication newAuth = new UsernamePasswordAuthenticationToken(
-                auth.getPrincipal(), auth.getCredentials(), authorities);
-        SecurityContextHolder.getContext().setAuthentication(newAuth);
-    }
-
-    /**
-     * Private default constructor, for static-only classes.
-     */
-    private AuthContextUtil() {
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/65d652af/core/misc/src/main/java/org/apache/syncope/core/misc/security/AuthContextUtils.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/security/AuthContextUtils.java b/core/misc/src/main/java/org/apache/syncope/core/misc/security/AuthContextUtils.java
new file mode 100644
index 0000000..2bdf45a
--- /dev/null
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/security/AuthContextUtils.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.misc.security;
+
+import java.util.EnumMap;
+import java.util.Map;
+import java.util.Set;
+import org.apache.commons.collections4.MapUtils;
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.types.Entitlement;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.User;
+
+public final class AuthContextUtils {
+
+    public static String getAuthenticatedUsername() {
+        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+        return authentication == null ? SyncopeConstants.UNAUTHENTICATED : authentication.getName();
+    }
+
+    public static void updateAuthenticatedUsername(final String newUsername) {
+        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
+
+        Authentication newAuth = new UsernamePasswordAuthenticationToken(
+                new User(newUsername, "FAKE_PASSWORD", auth.getAuthorities()),
+                auth.getCredentials(), auth.getAuthorities());
+        SecurityContextHolder.getContext().setAuthentication(newAuth);
+    }
+
+    public static Map<Entitlement, Set<String>> getAuthorizations() {
+        Map<Entitlement, Set<String>> result = null;
+
+        final SecurityContext ctx = SecurityContextHolder.getContext();
+        if (ctx != null && ctx.getAuthentication() != null && ctx.getAuthentication().getAuthorities() != null) {
+            result = new EnumMap<>(Entitlement.class);
+            for (GrantedAuthority authority : ctx.getAuthentication().getAuthorities()) {
+                if (authority instanceof SyncopeGrantedAuthority) {
+                    result.put(
+                            SyncopeGrantedAuthority.class.cast(authority).getEntitlement(),
+                            SyncopeGrantedAuthority.class.cast(authority).getRealms());
+                }
+            }
+        }
+
+        return MapUtils.emptyIfNull(result);
+    }
+
+    /**
+     * Private default constructor, for static-only classes.
+     */
+    private AuthContextUtils() {
+    }
+}

http://git-wip-us.apache.org/repos/asf/syncope/blob/65d652af/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 8ed136b..8ad1967 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
@@ -26,10 +26,11 @@ import org.apache.syncope.common.lib.types.PasswordPolicySpec;
 import org.apache.syncope.core.persistence.api.dao.PolicyDAO;
 import org.apache.syncope.core.persistence.api.entity.ExternalResource;
 import org.apache.syncope.core.persistence.api.entity.PasswordPolicy;
-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.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.entity.Realm;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
@@ -46,6 +47,9 @@ public class PasswordGenerator {
     @Autowired
     private PolicyDAO policyDAO;
 
+    @Autowired
+    private RealmDAO realmDAO;
+
     public String generate(final List<PasswordPolicySpec> ppSpecs)
             throws InvalidPasswordPolicySpecException {
 
@@ -59,18 +63,13 @@ public class PasswordGenerator {
     public String generate(final User user)
             throws InvalidPasswordPolicySpecException {
 
-        List<PasswordPolicySpec> ppSpecs = new ArrayList<PasswordPolicySpec>();
+        List<PasswordPolicySpec> ppSpecs = new ArrayList<>();
 
-        PasswordPolicy globalPP = policyDAO.getGlobalPasswordPolicy();
-        if (globalPP != null && globalPP.getSpecification(PasswordPolicySpec.class) != null) {
-            ppSpecs.add(globalPP.getSpecification(PasswordPolicySpec.class));
-        }
+        for (Realm ancestor : realmDAO.findAncestors(user.getRealm())) {
+            if (ancestor.getPasswordPolicy() != null
+                    && ancestor.getPasswordPolicy().getSpecification(PasswordPolicySpec.class) != null) {
 
-        for (Group group : user.getGroups()) {
-            if (group.getPasswordPolicy() != null
-                    && group.getPasswordPolicy().getSpecification(PasswordPolicySpec.class) != null) {
-
-                ppSpecs.add(group.getPasswordPolicy().getSpecification(PasswordPolicySpec.class));
+                ppSpecs.add(ancestor.getPasswordPolicy().getSpecification(PasswordPolicySpec.class));
             }
         }
 
@@ -220,7 +219,8 @@ public class PasswordGenerator {
         //filled empty chars
         for (int firstEmptyChar = firstEmptyChar(generatedPassword);
                 firstEmptyChar < generatedPassword.length - 1; firstEmptyChar++) {
-            generatedPassword[firstEmptyChar] = SecureRandomUtil.generateRandomLetter();
+
+            generatedPassword[firstEmptyChar] = SecureRandomUtils.generateRandomLetter();
         }
 
         checkPrefixAndSuffix(generatedPassword, policySpec);
@@ -230,42 +230,38 @@ public class PasswordGenerator {
 
     private void checkStartChar(final String[] generatedPassword, final PasswordPolicySpec policySpec) {
         if (policySpec.isMustStartWithAlpha()) {
-            generatedPassword[0] = SecureRandomUtil.generateRandomLetter();
+            generatedPassword[0] = SecureRandomUtils.generateRandomLetter();
         }
         if (policySpec.isMustStartWithNonAlpha() || policySpec.isMustStartWithDigit()) {
-            generatedPassword[0] = SecureRandomUtil.generateRandomNumber();
+            generatedPassword[0] = SecureRandomUtils.generateRandomNumber();
         }
         if (policySpec.isMustntStartWithAlpha()) {
-            generatedPassword[0] = SecureRandomUtil.generateRandomNumber();
-
+            generatedPassword[0] = SecureRandomUtils.generateRandomNumber();
         }
         if (policySpec.isMustntStartWithDigit()) {
-            generatedPassword[0] = SecureRandomUtil.generateRandomLetter();
-
+            generatedPassword[0] = SecureRandomUtils.generateRandomLetter();
         }
         if (policySpec.isMustntStartWithNonAlpha()) {
-            generatedPassword[0] = SecureRandomUtil.generateRandomLetter();
-
+            generatedPassword[0] = SecureRandomUtils.generateRandomLetter();
         }
     }
 
     private void checkEndChar(final String[] generatedPassword, final PasswordPolicySpec policySpec) {
         if (policySpec.isMustEndWithAlpha()) {
-            generatedPassword[policySpec.getMinLength() - 1] = SecureRandomUtil.generateRandomLetter();
+            generatedPassword[policySpec.getMinLength() - 1] = SecureRandomUtils.generateRandomLetter();
         }
         if (policySpec.isMustEndWithNonAlpha() || policySpec.isMustEndWithDigit()) {
-            generatedPassword[policySpec.getMinLength() - 1] = SecureRandomUtil.generateRandomNumber();
+            generatedPassword[policySpec.getMinLength() - 1] = SecureRandomUtils.generateRandomNumber();
         }
 
         if (policySpec.isMustntEndWithAlpha()) {
-            generatedPassword[policySpec.getMinLength() - 1] = SecureRandomUtil.generateRandomNumber();
+            generatedPassword[policySpec.getMinLength() - 1] = SecureRandomUtils.generateRandomNumber();
         }
         if (policySpec.isMustntEndWithDigit()) {
-            generatedPassword[policySpec.getMinLength() - 1] = SecureRandomUtil.generateRandomLetter();
+            generatedPassword[policySpec.getMinLength() - 1] = SecureRandomUtils.generateRandomLetter();
         }
         if (policySpec.isMustntEndWithNonAlpha()) {
-            generatedPassword[policySpec.getMinLength() - 1] = SecureRandomUtil.generateRandomLetter();
-
+            generatedPassword[policySpec.getMinLength() - 1] = SecureRandomUtils.generateRandomLetter();
         }
     }
 
@@ -281,28 +277,28 @@ public class PasswordGenerator {
         if (policySpec.isDigitRequired()
                 && !PolicyPattern.DIGIT.matcher(StringUtils.join(generatedPassword)).matches()) {
 
-            generatedPassword[firstEmptyChar(generatedPassword)] = SecureRandomUtil.generateRandomNumber();
+            generatedPassword[firstEmptyChar(generatedPassword)] = SecureRandomUtils.generateRandomNumber();
         }
 
         if (policySpec.isUppercaseRequired()
                 && !PolicyPattern.ALPHA_UPPERCASE.matcher(StringUtils.join(generatedPassword)).matches()) {
 
             generatedPassword[firstEmptyChar(generatedPassword)] =
-                    SecureRandomUtil.generateRandomLetter().toUpperCase();
+                    SecureRandomUtils.generateRandomLetter().toUpperCase();
         }
 
         if (policySpec.isLowercaseRequired()
                 && !PolicyPattern.ALPHA_LOWERCASE.matcher(StringUtils.join(generatedPassword)).matches()) {
 
             generatedPassword[firstEmptyChar(generatedPassword)] =
-                    SecureRandomUtil.generateRandomLetter().toLowerCase();
+                    SecureRandomUtils.generateRandomLetter().toLowerCase();
         }
 
         if (policySpec.isNonAlphanumericRequired()
                 && !PolicyPattern.NON_ALPHANUMERIC.matcher(StringUtils.join(generatedPassword)).matches()) {
 
             generatedPassword[firstEmptyChar(generatedPassword)] =
-                    SecureRandomUtil.generateRandomSpecialCharacter(SPECIAL_CHARS);
+                    SecureRandomUtils.generateRandomSpecialCharacter(SPECIAL_CHARS);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/syncope/blob/65d652af/core/misc/src/main/java/org/apache/syncope/core/misc/security/SecureRandomUtil.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/security/SecureRandomUtil.java b/core/misc/src/main/java/org/apache/syncope/core/misc/security/SecureRandomUtil.java
deleted file mode 100644
index 7939a83..0000000
--- a/core/misc/src/main/java/org/apache/syncope/core/misc/security/SecureRandomUtil.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.misc.security;
-
-import java.security.SecureRandom;
-
-import org.apache.commons.lang3.RandomStringUtils;
-
-public final class SecureRandomUtil {
-
-    private static final SecureRandom RANDOM = new SecureRandom();
-
-    public static String generateRandomPassword(final int tokenLength) {
-        return RandomStringUtils.random(tokenLength, 0, 0, true, false, null, RANDOM);
-    }
-
-    public static String generateRandomLetter() {
-        return RandomStringUtils.random(1, 0, 0, true, false, null, RANDOM);
-    }
-
-    public static String generateRandomNumber() {
-        return RandomStringUtils.random(1, 0, 0, false, true, null, RANDOM);
-    }
-
-    public static String generateRandomSpecialCharacter(final char[] characters) {
-        return RandomStringUtils.random(1, 0, 0, false, false, characters, RANDOM);
-    }
-
-    private SecureRandomUtil() {
-        // private constructor for static utility class
-    }
-}

http://git-wip-us.apache.org/repos/asf/syncope/blob/65d652af/core/misc/src/main/java/org/apache/syncope/core/misc/security/SecureRandomUtils.java
----------------------------------------------------------------------
diff --git a/core/misc/src/main/java/org/apache/syncope/core/misc/security/SecureRandomUtils.java b/core/misc/src/main/java/org/apache/syncope/core/misc/security/SecureRandomUtils.java
new file mode 100644
index 0000000..f41207b
--- /dev/null
+++ b/core/misc/src/main/java/org/apache/syncope/core/misc/security/SecureRandomUtils.java
@@ -0,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.core.misc.security;
+
+import java.security.SecureRandom;
+
+import org.apache.commons.lang3.RandomStringUtils;
+
+public final class SecureRandomUtils {
+
+    private static final SecureRandom RANDOM = new SecureRandom();
+
+    public static String generateRandomPassword(final int tokenLength) {
+        return RandomStringUtils.random(tokenLength, 0, 0, true, false, null, RANDOM);
+    }
+
+    public static String generateRandomLetter() {
+        return RandomStringUtils.random(1, 0, 0, true, false, null, RANDOM);
+    }
+
+    public static String generateRandomNumber() {
+        return RandomStringUtils.random(1, 0, 0, false, true, null, RANDOM);
+    }
+
+    public static String generateRandomSpecialCharacter(final char[] characters) {
+        return RandomStringUtils.random(1, 0, 0, false, false, characters, RANDOM);
+    }
+
+    private SecureRandomUtils() {
+        // private constructor for static utility class
+    }
+}