org.oscm.identityservice.bean.IdentityServiceBean.java Source code

Java tutorial

Introduction

Here is the source code for org.oscm.identityservice.bean.IdentityServiceBean.java

Source

/*******************************************************************************
 *                                                                              
 *  Copyright FUJITSU LIMITED 2016                                             
 *                                                                              
 *  Author: Mike Jäger                                                      
 *                                                                              
 *  Creation Date: 12.02.2009                                                      
 *                                                                              
 *  Completion Time: 16.03.2009                                             
 *                                                                              
 *******************************************************************************/

package org.oscm.identityservice.bean;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Random;
import java.util.Set;

import javax.annotation.Resource;
import javax.annotation.security.DeclareRoles;
import javax.annotation.security.RolesAllowed;
import javax.ejb.Asynchronous;
import javax.ejb.EJB;
import javax.ejb.Local;
import javax.ejb.Remote;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;
import javax.interceptor.Interceptors;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.Query;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.validator.GenericValidator;
import org.oscm.authorization.PasswordHash;
import org.oscm.communicationservice.data.SendMailStatus;
import org.oscm.communicationservice.data.SendMailStatus.SendMailStatusItem;
import org.oscm.communicationservice.local.CommunicationServiceLocal;
import org.oscm.configurationservice.local.ConfigurationServiceLocal;
import org.oscm.converter.ParameterEncoder;
import org.oscm.converter.ParameterizedTypes;
import org.oscm.dataservice.local.DataService;
import org.oscm.domobjects.ConfigurationSetting;
import org.oscm.domobjects.Marketplace;
import org.oscm.domobjects.OnBehalfUserReference;
import org.oscm.domobjects.Organization;
import org.oscm.domobjects.PlatformUser;
import org.oscm.domobjects.RoleAssignment;
import org.oscm.domobjects.Session;
import org.oscm.domobjects.Subscription;
import org.oscm.domobjects.Tenant;
import org.oscm.domobjects.TriggerDefinition;
import org.oscm.domobjects.TriggerProcess;
import org.oscm.domobjects.UnitUserRole;
import org.oscm.domobjects.UsageLicense;
import org.oscm.domobjects.UserGroup;
import org.oscm.domobjects.UserRole;
import org.oscm.domobjects.enums.ModificationType;
import org.oscm.id.IdGenerator;
import org.oscm.identityservice.assembler.UserDataAssembler;
import org.oscm.identityservice.bean.BulkUserImportReader.Row;
import org.oscm.identityservice.control.SendMailControl;
import org.oscm.identityservice.ldap.UserModificationCheck;
import org.oscm.identityservice.local.ILdapResultMapper;
import org.oscm.identityservice.local.IdentityServiceLocal;
import org.oscm.identityservice.local.LdapAccessServiceLocal;
import org.oscm.identityservice.local.LdapConnector;
import org.oscm.identityservice.local.LdapSettingsManagementServiceLocal;
import org.oscm.identityservice.local.LdapVOUserDetailsMapper;
import org.oscm.identityservice.pwdgen.PasswordGenerator;
import org.oscm.interceptor.DateFactory;
import org.oscm.interceptor.ExceptionMapper;
import org.oscm.interceptor.InvocationDateContainer;
import org.oscm.interceptor.PlatformOperatorServiceProviderInterceptor;
import org.oscm.interceptor.ServiceProviderInterceptor;
import org.oscm.internal.intf.IdentityService;
import org.oscm.internal.types.enumtypes.ConfigurationKey;
import org.oscm.internal.types.enumtypes.OrganizationRoleType;
import org.oscm.internal.types.enumtypes.SettingType;
import org.oscm.internal.types.enumtypes.SubscriptionStatus;
import org.oscm.internal.types.enumtypes.TriggerType;
import org.oscm.internal.types.enumtypes.UnitRoleType;
import org.oscm.internal.types.enumtypes.UserAccountStatus;
import org.oscm.internal.types.enumtypes.UserRoleType;
import org.oscm.internal.types.exception.BulkUserImportException;
import org.oscm.internal.types.exception.ConcurrentModificationException;
import org.oscm.internal.types.exception.DomainObjectException.ClassEnum;
import org.oscm.internal.types.exception.IllegalArgumentException;
import org.oscm.internal.types.exception.MailOperationException;
import org.oscm.internal.types.exception.NonUniqueBusinessKeyException;
import org.oscm.internal.types.exception.ObjectNotFoundException;
import org.oscm.internal.types.exception.OperationNotPermittedException;
import org.oscm.internal.types.exception.OperationPendingException;
import org.oscm.internal.types.exception.OrganizationAuthoritiesException;
import org.oscm.internal.types.exception.OrganizationRemovedException;
import org.oscm.internal.types.exception.SaaSSystemException;
import org.oscm.internal.types.exception.SecurityCheckException;
import org.oscm.internal.types.exception.TechnicalServiceNotAliveException;
import org.oscm.internal.types.exception.TechnicalServiceOperationException;
import org.oscm.internal.types.exception.UnsupportedOperationException;
import org.oscm.internal.types.exception.UserActiveException;
import org.oscm.internal.types.exception.UserDeletionConstraintException;
import org.oscm.internal.types.exception.UserDeletionConstraintException.Reason;
import org.oscm.internal.types.exception.UserModificationConstraintException;
import org.oscm.internal.types.exception.UserRoleAssignmentException;
import org.oscm.internal.types.exception.ValidationException;
import org.oscm.internal.types.exception.ValidationException.ReasonEnum;
import org.oscm.internal.vo.VOUser;
import org.oscm.internal.vo.VOUserDetails;
import org.oscm.logging.Log4jLogger;
import org.oscm.logging.LoggerFactory;
import org.oscm.permission.PermissionCheck;
import org.oscm.reviewservice.bean.ReviewServiceLocalBean;
import org.oscm.sessionservice.local.SessionServiceLocal;
import org.oscm.stream.Streams;
import org.oscm.string.Strings;
import org.oscm.subscriptionservice.local.SubscriptionServiceLocal;
import org.oscm.taskhandling.local.TaskMessage;
import org.oscm.taskhandling.local.TaskQueueServiceLocal;
import org.oscm.taskhandling.operations.ImportUserHandler;
import org.oscm.taskhandling.operations.UpdateUserHandler;
import org.oscm.taskhandling.payloads.ImportUserPayload;
import org.oscm.taskhandling.payloads.UpdateUserPayload;
import org.oscm.triggerservice.bean.TriggerProcessIdentifiers;
import org.oscm.triggerservice.local.TriggerMessage;
import org.oscm.triggerservice.local.TriggerProcessMessageData;
import org.oscm.triggerservice.local.TriggerQueueServiceLocal;
import org.oscm.triggerservice.validator.TriggerProcessValidator;
import org.oscm.types.constants.Configuration;
import org.oscm.types.enumtypes.EmailType;
import org.oscm.types.enumtypes.LogMessageIdentifier;
import org.oscm.types.enumtypes.TriggerProcessParameterName;
import org.oscm.usergroupservice.bean.UserGroupServiceLocalBean;
import org.oscm.validation.ArgumentValidator;
import org.oscm.validator.BLValidator;
import org.oscm.vo.BaseAssembler;

/**
 * Session Bean implementation class IdentityServiceBean
 */
@DeclareRoles("ORGANIZATION_ADMIN")
@EJB(name = "ConfigurationService", beanInterface = ConfigurationServiceLocal.class)
@Stateless
@Remote(IdentityService.class)
@Local(IdentityServiceLocal.class)
@Interceptors({ InvocationDateContainer.class, ExceptionMapper.class })
public class IdentityServiceBean implements IdentityService, IdentityServiceLocal {

    private static final Log4jLogger logger = LoggerFactory.getLogger(IdentityServiceBean.class);

    private static final Random random = new SecureRandom();

    @EJB(beanInterface = DataService.class)
    protected DataService dm;

    @EJB(beanInterface = SubscriptionServiceLocal.class)
    SubscriptionServiceLocal sm;

    @EJB(beanInterface = CommunicationServiceLocal.class)
    CommunicationServiceLocal cm;

    @EJB(beanInterface = ConfigurationServiceLocal.class)
    ConfigurationServiceLocal cs;

    @EJB(beanInterface = SessionServiceLocal.class)
    private SessionServiceLocal prodSessionMgmt;

    @EJB(beanInterface = LdapAccessServiceLocal.class)
    public LdapAccessServiceLocal ldapAccess;

    @EJB(beanInterface = TaskQueueServiceLocal.class)
    public TaskQueueServiceLocal tqs;

    @Inject
    public ReviewServiceLocalBean rvs;

    @Inject
    UserGroupServiceLocalBean userGroupService;

    @EJB(beanInterface = TriggerQueueServiceLocal.class)
    protected TriggerQueueServiceLocal triggerQS;

    @EJB(beanInterface = LdapSettingsManagementServiceLocal.class)
    protected LdapSettingsManagementServiceLocal ldapSettingsMS;

    @EJB(beanInterface = SessionServiceLocal.class)
    SessionServiceLocal sessionService;

    @Resource
    SessionContext sessionCtx;

    public IdentityServiceBean() {

    }

    @Override
    @RolesAllowed("ORGANIZATION_ADMIN")
    public VOUserDetails createUser(VOUserDetails user, List<UserRoleType> roles, String marketplaceId)
            throws NonUniqueBusinessKeyException, MailOperationException, ValidationException,
            UserRoleAssignmentException, OperationPendingException {

        return createUser(user, roles, marketplaceId, null);
    }

    @Override
    @RolesAllowed("ORGANIZATION_ADMIN")
    public VOUserDetails createUser(VOUserDetails user, String marketplaceId) throws NonUniqueBusinessKeyException,
            MailOperationException, ValidationException, UserRoleAssignmentException, OperationPendingException {

        List<UserRoleType> roles = Collections.emptyList();
        return createUser(user, roles, marketplaceId, null);
    }

    @Override
    @RolesAllowed("ORGANIZATION_ADMIN")
    public void importUsersInOwnOrganization(byte[] csvData, String marketplaceId)
            throws BulkUserImportException, ObjectNotFoundException, IllegalArgumentException {

        ArgumentValidator.notNull("csvData", csvData);

        Organization organization = dm.getCurrentUser().getOrganization();

        importUsers(csvData, organization, marketplaceId);
    }

    @Override
    @RolesAllowed("PLATFORM_OPERATOR")
    public void importUsers(byte[] csvData, String organizationId, String marketplaceId)
            throws BulkUserImportException, ObjectNotFoundException, IllegalArgumentException {

        ArgumentValidator.notNull("csvData", csvData);
        ArgumentValidator.notNull("organizationId", organizationId);

        Organization organization = getOrganization(organizationId);

        importUsers(csvData, organization, marketplaceId);
    }

    /**
     * @param csvData
     * @param marketplaceId
     * @param organization
     * @throws BulkUserImportException
     * @throws ObjectNotFoundException
     */
    protected void importUsers(byte[] csvData, Organization organization, String marketplaceId)
            throws BulkUserImportException, ObjectNotFoundException {
        // preconditions
        BulkUserImportReader.validate(csvData);
        ensureNoRemoteLdapUsed(organization, LogMessageIdentifier.ERROR_ADDING_USER_FORBIDDEN_REMOTE_LDAP);
        ensureEmailSet();
        if (marketplaceId != null && marketplaceId.trim().length() != 0) {
            checkIfMarketplaceExists(marketplaceId);
        }

        // payload
        ImportUserPayload payload = new ImportUserPayload();
        payload.setImportingUserKey(Long.valueOf(dm.getCurrentUser().getKey()));
        payload.setOrganizationId(organization.getOrganizationId());
        payload.setMarketplaceId(marketplaceId);

        // read csv data
        BulkUserImportReader reader = null;
        try {
            reader = new BulkUserImportReader(csvData);
            for (Row row : reader) {
                VOUserDetails userDetails = row.getUserDetails();
                List<UserRoleType> roles = row.getRoles();
                payload.addUser(userDetails, roles);
            }
        } finally {
            Streams.close(reader);
        }

        // create async task
        TaskMessage message = new TaskMessage(ImportUserHandler.class, payload);
        tqs.sendAllMessages(Collections.singletonList(message));
    }

    /**
     * The importing user must have an email defined. Otherwise, the import
     * report cannot be sent by mail.
     */
    void ensureEmailSet() throws BulkUserImportException {
        if (Strings.isEmpty(dm.getCurrentUser().getEmail())) {
            throw new BulkUserImportException(BulkUserImportException.Reason.EMAIL_REQUIRED, null);
        }
    }

    @Override
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void importUser(VOUserDetails user, String marketplaceId) throws NonUniqueBusinessKeyException,
            MailOperationException, ValidationException, UserRoleAssignmentException, ObjectNotFoundException {
        try {
            Marketplace marketplace = getMarketplace(marketplaceId);
            Organization organization = getOrganization(user.getOrganizationId());
            String password = new PasswordGenerator().generatePassword();
            addPlatformUser(user, organization, password, UserAccountStatus.PASSWORD_MUST_BE_CHANGED, true, true,
                    marketplace, true);
        } catch (NonUniqueBusinessKeyException | MailOperationException | ValidationException
                | UserRoleAssignmentException e) {
            sessionCtx.setRollbackOnly();
            throw e;
        }
    }

    /**
     * Bug 9324 Trigger should not be fired if user exists
     */
    private void checkIfUserExists(String userId, Tenant tenant) throws NonUniqueBusinessKeyException {
        PlatformUser dbUser = loadUser(userId, tenant);
        if (dbUser != null) {
            throw new NonUniqueBusinessKeyException(ClassEnum.USER, userId);
        }
    }

    /**
     * Loads a user from database.
     * 
     * @param userId
     *            user id
     * @return the user otherwise null if the user does not exist.
     */
    PlatformUser loadUser(String userId, Tenant tenant) {
        try {
            // if (tenant == null) {
            PlatformUser user = new PlatformUser();
            user.setUserId(userId);
            if (tenant != null) {
                user.setTenantId(tenant.getTenantId());
            }
            return (PlatformUser) dm.getReferenceByBusinessKey(user);
            // }
            // Query q =
            // dm.createNamedQuery("PlatformUser.findByUserIdAndTenant");
            // q.setParameter("userId", userId);
            // q.setParameter("tenantId", tenant.getTenantId());
            // return (PlatformUser) q.getSingleResult();
        } catch (ObjectNotFoundException | NoResultException e) {
            return null;
        }
    }

    @Override
    public VOUserDetails createUserInt(TriggerProcess tp) throws NonUniqueBusinessKeyException,
            MailOperationException, ValidationException, UserRoleAssignmentException {

        VOUserDetails user = tp.getParamValueForName(TriggerProcessParameterName.USER)
                .getValue(VOUserDetails.class);
        List<UserRoleType> roles = ParameterizedTypes.list(
                tp.getParamValueForName(TriggerProcessParameterName.USER_ROLE_TYPE).getValue(List.class),
                UserRoleType.class);
        String marketplaceId = tp.getParamValueForName(TriggerProcessParameterName.MARKETPLACE_ID)
                .getValue(String.class);
        Map<Long, UnitUserRole> userGroupKeyToRole = ParameterizedTypes.hashmap(
                tp.getParamValueForName(TriggerProcessParameterName.USER_GROUPS_WITH_ROLES).getValue(Map.class),
                Long.class, UnitUserRole.class);

        // generate a one-time password for the user
        VOUserDetails result;
        Organization organization = dm.getCurrentUser().getOrganization();
        try {
            ensureNoRemoteLdapUsed(organization, LogMessageIdentifier.ERROR_ADDING_USER_FORBIDDEN_REMOTE_LDAP);
            Marketplace marketplace = getMarketplace(marketplaceId);
            PasswordGenerator gen = new PasswordGenerator();
            String password = gen.generatePassword();

            // set the user roles also into the VO
            // because addPlatformUser() checks them
            user.setUserRoles(new HashSet<>(roles));
            PlatformUser addedUser = addPlatformUser(user, organization, password,
                    UserAccountStatus.PASSWORD_MUST_BE_CHANGED, true, true, marketplace, true);
            if (userGroupKeyToRole != null && !userGroupKeyToRole.isEmpty()) {
                Map<UserGroup, UnitUserRole> userToGroup = getUserGroupToRoleMap(userGroupKeyToRole);
                userGroupService.assignUserToGroups(addedUser, userToGroup);
                List<UnitRoleType> unitRoleTypes = new ArrayList<UnitRoleType>();
                for (Entry<Long, UnitUserRole> groupWithRole : userGroupKeyToRole.entrySet()) {
                    unitRoleTypes.add(groupWithRole.getValue().getRoleName());
                }
                for (Entry<UserGroup, UnitUserRole> groupWithRole : userToGroup.entrySet()) {
                    userGroupService.grantUserRoles(addedUser,
                            Arrays.asList(groupWithRole.getValue().getRoleName()), groupWithRole.getKey());
                }
                if (unitRoleTypes.contains(UnitRoleType.ADMINISTRATOR)) {
                    grantRole(addedUser, UserRoleType.UNIT_ADMINISTRATOR);
                }
            }
            result = UserDataAssembler.toVOUserDetails(addedUser);
        } catch (NonUniqueBusinessKeyException | MailOperationException | ValidationException
                | UserRoleAssignmentException e) {
            sessionCtx.setRollbackOnly();
            throw e;
        } catch (ObjectNotFoundException | OperationNotPermittedException e) {
            logger.logError(Log4jLogger.SYSTEM_LOG, e, LogMessageIdentifier.ERROR);
            throw new ValidationException(e.getMessage());
        }

        triggerQS.sendAllNonSuspendingMessages(TriggerMessage.create(TriggerType.REGISTER_OWN_USER,
                tp.getTriggerProcessParameters(), dm.getCurrentUser().getOrganization()));

        return result;
    }

    private Map<UserGroup, UnitUserRole> getUserGroupToRoleMap(Map<Long, UnitUserRole> userGroupKeyToRole) {
        Map<UserGroup, UnitUserRole> userGroupToRole = new HashMap<>();
        if (userGroupKeyToRole != null) {
            for (Map.Entry<Long, UnitUserRole> e : userGroupKeyToRole.entrySet()) {
                try {
                    userGroupToRole.put(dm.getReference(UserGroup.class, e.getKey().longValue()), e.getValue());
                } catch (ObjectNotFoundException ignored) {
                }
            }
        }
        return userGroupToRole;
    }

    @Override
    @Interceptors({ PlatformOperatorServiceProviderInterceptor.class })
    public void changePassword(String oldPassword, String newPassword)
            throws SecurityCheckException, ValidationException {

        ArgumentValidator.notNull("oldPassword", oldPassword);
        ArgumentValidator.notNull("newPassword", newPassword);
        BLValidator.isPassword("newPassword", newPassword);
        PlatformUser pUser = dm.getCurrentUser();
        ensureNoRemoteLdapUsed(pUser.getOrganization(),
                LogMessageIdentifier.ERROR_PASSWORD_OPERATION_FORBIDDEN_REMOTE_LDAP);

        // two steps have to be performed:
        // 1. validate old password
        if (!PasswordHash.verifyPassword(pUser.getPasswordSalt(), pUser.getPasswordHash(), oldPassword)) {
            // So the authentication failed. Log the problem, increase the
            // user's failed login counter and throw the exception and throw an
            // exception.
            SecurityCheckException scf = new SecurityCheckException(
                    "Authentication failed, a wrong password has been specified.");
            logger.logWarn(Log4jLogger.SYSTEM_LOG, scf, LogMessageIdentifier.WARN_CHANGE_PASSWORD_FAILED);
            int count = pUser.getFailedLoginCounter();
            count++;
            pUser.setFailedLoginCounter(count);
            if (count >= getMaxRetryAttempts()) {
                pUser.setStatus(UserAccountStatus.LOCKED_FAILED_LOGIN_ATTEMPTS);
            }
            scf.setMessageKey("error.changePassword");
            throw scf;
        }

        // 2. change password to new one
        setPassword(pUser, newPassword);

        // 3. if the user account status is indicating that the password must be
        // changed, revert this setting to active
        if (pUser.getStatus() == UserAccountStatus.PASSWORD_MUST_BE_CHANGED) {
            pUser.setStatus(UserAccountStatus.ACTIVE);
        }

    }

    @Override
    public void confirmAccount(VOUser user, String marketplaceId)
            throws OperationNotPermittedException, ObjectNotFoundException, MailOperationException {
        // user is not logged in, so don't log user and organization key
        // information

        ArgumentValidator.notNull("user", user);

        // confirmation means releasing the lock of type
        // UserAccountStatus.LOCKED_NOT_CONFIRMED
        PlatformUser pUser = dm.getReference(PlatformUser.class, user.getKey());
        UserAccountStatus currentStatus = pUser.getStatus();
        if (currentStatus == UserAccountStatus.LOCKED_NOT_CONFIRMED) {
            pUser.setStatus(UserAccountStatus.ACTIVE);

            // send acknowledge e-mail
            try {
                if (pUser.hasManagerRole()) {
                    cm.sendMail(pUser, EmailType.USER_CONFIRM_ACKNOWLEDGE,
                            new Object[] { cm.getBaseUrl(), pUser.getUserId(), String.valueOf(pUser.getKey()) },
                            getMarketplace(marketplaceId));
                } else {
                    cm.sendMail(pUser, EmailType.USER_CONFIRM_ACKNOWLEDGE,
                            new Object[] { cm.getMarketplaceUrl(marketplaceId), pUser.getUserId(),
                                    String.valueOf(pUser.getKey()) },
                            getMarketplace(marketplaceId));
                }
            } catch (MailOperationException e) {
                sessionCtx.setRollbackOnly();
                throw e;
            }
        }
        // do nothing if account is already active, but otherwise throw an
        // exception
        else if (currentStatus != UserAccountStatus.ACTIVE) {
            OperationNotPermittedException onp = new OperationNotPermittedException(
                    "The account of user '" + user.getUserId() + "' cannot be confirmed.");
            logger.logWarn(Log4jLogger.SYSTEM_LOG, onp, LogMessageIdentifier.WARN_CONFIRM_OPERATOR_ACCOUNT_FAILED,
                    user.getUserId());
            throw onp;
        }

    }

    @Override
    @RolesAllowed("ORGANIZATION_ADMIN")
    public void grantUserRoles(VOUser user, List<UserRoleType> roles)
            throws ObjectNotFoundException, OperationNotPermittedException, UserRoleAssignmentException {

        ArgumentValidator.notNull("user", user);
        ArgumentValidator.notNull("roles", roles);
        PlatformUser pUser = getPlatformUser(user.getUserId(), dm.getCurrentUser().getTenantId(), true);
        grantUserRoles(pUser, roles);

    }

    @Override
    @RolesAllowed({ "ORGANIZATION_ADMIN", "UNIT_ADMINISTRATOR" })
    public void grantUnitRole(VOUser user, UserRoleType role)
            throws ObjectNotFoundException, OperationNotPermittedException {

        ArgumentValidator.notNull("user", user);
        ArgumentValidator.notNull("role", role);
        PlatformUser pUser = getPlatformUser(user.getUserId(), dm.getCurrentUser().getTenantId(), true);
        grantUnitRole(pUser, role);
    }

    @Override
    @RolesAllowed({ "ORGANIZATION_ADMIN", "UNIT_ADMINISTRATOR" })
    public void revokeUnitRole(VOUser user, UserRoleType role)
            throws ObjectNotFoundException, OperationNotPermittedException {

        ArgumentValidator.notNull("user", user);
        ArgumentValidator.notNull("role", role);
        PlatformUser pUser = getPlatformUser(user.getUserId(), dm.getCurrentUser().getTenantId(), true);
        revokeUnitRole(pUser, role);
    }

    @Override
    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public void grantUserRoles(PlatformUser user, List<UserRoleType> roles)
            throws ObjectNotFoundException, OperationNotPermittedException, UserRoleAssignmentException {
        for (UserRoleType role : roles) {
            createUserRoleRelationInt(user, role);
        }
    }

    @Override
    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public void resetPasswordForUser(PlatformUser user, Marketplace marketplace) throws MailOperationException {

        ensureNoRemoteLdapUsed(user.getOrganization(),
                LogMessageIdentifier.ERROR_PASSWORD_OPERATION_FORBIDDEN_REMOTE_LDAP);

        final String newPwd = new PasswordGenerator().generatePassword();
        setPassword(user, newPwd);
        // send mail to the affected user
        try {
            cm.sendMail(user, EmailType.USER_PASSWORD_RESET, new Object[] { newPwd }, marketplace);
        } catch (MailOperationException e) {
            // Mail reception is essential, otherwise the user cannot work with
            // his account
            MailOperationException mof = new MailOperationException(
                    "Mail with your new password cannot be sent. Operation aborted!", e);
            logger.logWarn(Log4jLogger.SYSTEM_LOG, mof, LogMessageIdentifier.WARN_OPERATOR_RESET_PASSWORD_FAILED,
                    dm.getCurrentUser().getUserId(), user.getUserId(), e.getId());
            sessionCtx.setRollbackOnly();
            throw mof;
        }

        // unlock the account, if not locked by a platform operator
        if (user.getStatus().getLockLevel() <= UserAccountStatus.LOCKED.getLockLevel()) {
            user.setStatus(UserAccountStatus.PASSWORD_MUST_BE_CHANGED);
            user.setFailedLoginCounter(0);
        }

    }

    @Override
    @RolesAllowed("ORGANIZATION_ADMIN")
    @Interceptors({ ServiceProviderInterceptor.class })
    public void requestResetOfUserPassword(VOUser user, String marketplaceId)
            throws MailOperationException, ObjectNotFoundException, OperationNotPermittedException,
            UserActiveException, ConcurrentModificationException {

        ArgumentValidator.notNull("user", user);
        PlatformUser platformUser = getPlatformUser(user.getUserId(), user.getTenantId(), true);
        BaseAssembler.verifyVersionAndKey(platformUser, user);

        resetUserPassword(platformUser, marketplaceId);

    }

    @Override
    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public void resetUserPassword(PlatformUser platformUser, String marketplaceId)
            throws UserActiveException, MailOperationException {
        // determine if the user has an active session, if so, throw an
        // exception
        List<Session> sessionsForUserKey = prodSessionMgmt.getSessionsForUserKey(platformUser.getKey());
        if (sessionsForUserKey.size() > 0) {
            UserActiveException uae = new UserActiveException("Reset of password for user '" + platformUser.getKey()
                    + "' failed, as the user is still active", new Object[] { platformUser.getUserId() });
            logger.logWarn(Log4jLogger.SYSTEM_LOG, uae, LogMessageIdentifier.WARN_OPERATOR_RESET_PASSWORD_FAILED);
            throw uae;
        }

        // reset the password
        resetPasswordForUser(platformUser, getMarketplace(marketplaceId));
    }

    @Override
    @RolesAllowed("ORGANIZATION_ADMIN")
    public void deleteUser(VOUser user, String marketplaceId)
            throws UserDeletionConstraintException, ObjectNotFoundException, ConcurrentModificationException,
            OperationNotPermittedException, TechnicalServiceNotAliveException, TechnicalServiceOperationException {

        ArgumentValidator.notNull("user", user);
        PlatformUser pUser = dm.getReference(PlatformUser.class, user.getKey());
        BaseAssembler.verifyVersionAndKey(pUser, user);

        deleteUser(pUser, marketplaceId);

    }

    @Override
    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public void deleteUser(PlatformUser pUser, String marketplaceId) throws OperationNotPermittedException,
            UserDeletionConstraintException, TechnicalServiceNotAliveException, TechnicalServiceOperationException {
        // Check whether user belongs to same organization than the caller
        PermissionCheck.sameOrg(dm.getCurrentUser(), pUser, logger);
        deletePlatformUser(pUser, false, false, getMarketplace(marketplaceId));
    }

    @Override
    @RolesAllowed("ORGANIZATION_ADMIN")
    public void revokeUserRoles(VOUser user, List<UserRoleType> roles)
            throws ObjectNotFoundException, UserModificationConstraintException, UserActiveException,
            OperationNotPermittedException, UserRoleAssignmentException {

        ArgumentValidator.notNull("user", user);
        ArgumentValidator.notNull("roles", roles);

        PlatformUser pUser = dm.getReference(PlatformUser.class, user.getKey());
        revokeUserRolesInt(pUser, roles);

    }

    private void revokeUserRolesInt(PlatformUser user, List<UserRoleType> roles)
            throws UserModificationConstraintException, UserActiveException, OperationNotPermittedException,
            UserRoleAssignmentException {
        if (roles.isEmpty()) {
            return;
        }

        // Bug #7223: Determine if the specified user is not the user calling
        // this method and has an active session. If so, throw an exception,
        // because updating the roles of another user is not possible
        if (dm.getCurrentUser().getKey() != user.getKey()) {
            List<Session> sessionsForUserKey = prodSessionMgmt.getSessionsForUserKey(user.getKey());
            if (sessionsForUserKey.size() > 0) {
                UserActiveException uae = new UserActiveException(
                        "Revoking of roles for user '" + user.getKey() + "' failed, as the user is still active",
                        new Object[] { user.getUserId() });
                logger.logWarn(Log4jLogger.SYSTEM_LOG, uae, LogMessageIdentifier.WARN_REVOKE_USER_ROLE_FAILED,
                        user.getUserId());
                throw uae;
            }
        }

        // check whether user belongs to same organization than the caller
        PermissionCheck.sameOrg(dm.getCurrentUser(), user, logger);

        for (UserRoleType role : roles) {
            // if the user is revoked the organization admin role, the
            // corresponding domain object has to be updated
            checkRoleConstrains(user, role);
            revokeRole(user, role);
        }
    }

    @Override
    @RolesAllowed("ORGANIZATION_ADMIN")
    public VOUserDetails getUserDetails(VOUser user)
            throws ObjectNotFoundException, OperationNotPermittedException {

        ArgumentValidator.notNull("user", user);
        PlatformUser pUser = getPlatformUser(user.getUserId(), dm.getCurrentUser().getTenantId(), true);

        return UserDataAssembler.toVOUserDetails(pUser);
    }

    @Override
    @RolesAllowed({ "ORGANIZATION_ADMIN", "SUBSCRIPTION_MANAGER", "UNIT_ADMINISTRATOR" })
    public List<VOUserDetails> getUsersForOrganization() {
        List<PlatformUser> organizationUsers = getOrganizationUsers();
        List<VOUserDetails> resultList = new ArrayList<>();
        for (PlatformUser currentUser : organizationUsers) {
            resultList.add(UserDataAssembler.toVOUserDetails(currentUser));
        }
        return resultList;
    }

    @Override
    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public List<PlatformUser> getOrganizationUsers() {

        // The organization is determined by the currently logged in user. To
        // obtain the users for this organization, the organization domain
        // object has to be loaded, which then references the users.
        PlatformUser user = dm.getCurrentUser();

        // 1. determine the correlating organization
        Organization organization = user.getOrganization();

        Query q = dm.createNamedQuery("PlatformUser.getVisibleForOrganization");
        q.setParameter("organization", organization);

        return ParameterizedTypes.list(q.getResultList(), PlatformUser.class);
    }

    @Override
    @RolesAllowed("ORGANIZATION_ADMIN")
    public void lockUserAccount(VOUser user, UserAccountStatus newStatus, String marketplaceId)
            throws OperationNotPermittedException, ObjectNotFoundException, ConcurrentModificationException {

        ArgumentValidator.notNull("user", user);
        ArgumentValidator.notNull("newStatus", newStatus);

        // get the corresponding platform user
        PlatformUser userObj = dm.getReference(PlatformUser.class, user.getKey());

        // check whether user belongs to same organization than the caller
        PermissionCheck.sameOrg(dm.getCurrentUser(), userObj, logger);

        ensureNoRemoteLdapUsed(userObj.getOrganization(),
                LogMessageIdentifier.ERROR_LOCK_OPERATION_FORBIDDEN_REMOTE_LDAP);

        BaseAssembler.verifyVersionAndKey(userObj, user);

        // ensure that the given lock type is really a locking type
        if (newStatus == UserAccountStatus.ACTIVE || newStatus == UserAccountStatus.PASSWORD_MUST_BE_CHANGED) {
            OperationNotPermittedException onp = new OperationNotPermittedException(
                    "To lock an account a locking state must be set!");
            logger.logWarn(Log4jLogger.SYSTEM_LOG | Log4jLogger.AUDIT_LOG, onp,
                    LogMessageIdentifier.WARN_OPERATOR_LOCK_FAILED, dm.getCurrentUser().getUserId(),
                    userObj.getUserId());
            throw onp;
        }
        if (newStatus == UserAccountStatus.LOCKED_FAILED_LOGIN_ATTEMPTS
                || newStatus == UserAccountStatus.LOCKED_NOT_CONFIRMED) {
            OperationNotPermittedException onp = new OperationNotPermittedException(
                    "A user must not set those locking states!");
            logger.logWarn(Log4jLogger.SYSTEM_LOG | Log4jLogger.AUDIT_LOG, onp,
                    LogMessageIdentifier.WARN_OPERATOR_LOCK_FAILED, dm.getCurrentUser().getUserId(),
                    userObj.getUserId());
            throw onp;
        }
        if (user.getStatus().getLockLevel() > newStatus.getLockLevel()) {
            OperationNotPermittedException onp = new OperationNotPermittedException(
                    "The lock level must not be reduced, current lock level is higher than the one specified!");
            logger.logWarn(Log4jLogger.SYSTEM_LOG | Log4jLogger.AUDIT_LOG, onp,
                    LogMessageIdentifier.WARN_OPERATOR_LOCK_FAILED, dm.getCurrentUser().getUserId(),
                    userObj.getUserId());
            throw onp;
        }
        setUserAccountStatusInternal(userObj, newStatus, getMarketplace(marketplaceId));

    }

    @Override
    public VOUserDetails updateUser(VOUserDetails user) throws OperationNotPermittedException,
            ObjectNotFoundException, ValidationException, NonUniqueBusinessKeyException,
            TechnicalServiceNotAliveException, TechnicalServiceOperationException, ConcurrentModificationException {

        ArgumentValidator.notNull("user", user);

        // 1. obtain the user's domain object.
        PlatformUser existingUser = dm.getReference(PlatformUser.class, user.getKey());

        final PlatformUser updatedUser;
        try {
            updatedUser = modifyUserData(existingUser, user, false, true);
        } catch (NonUniqueBusinessKeyException | TechnicalServiceNotAliveException
                | TechnicalServiceOperationException e) {
            sessionCtx.setRollbackOnly();
            throw e;
        }
        // enforce version update
        dm.flush();

        return UserDataAssembler.toVOUserDetails(updatedUser);
    }

    @Override
    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public PlatformUser modifyUserData(PlatformUser existingUser, VOUserDetails updatedUser, boolean modifyOwnUser,
            boolean sendMail)
            throws OperationNotPermittedException, NonUniqueBusinessKeyException, TechnicalServiceNotAliveException,
            TechnicalServiceOperationException, ValidationException, ConcurrentModificationException {

        // check whether user belongs to same organization than the caller
        PermissionCheck.sameOrg(dm.getCurrentUser(), existingUser, logger);

        PlatformUser oldUser = existingUser.getEmail() != null ? UserDataAssembler.copyPlatformUser(existingUser)
                : null;

        // validate permissions for the call, administrator may change any user,
        // a non administrator may only change his own account
        if (!dm.getCurrentUser().isOrganizationAdmin() || modifyOwnUser) {
            if (!String.valueOf(updatedUser.getKey()).equals(sessionCtx.getCallerPrincipal().getName())) {
                OperationNotPermittedException onp = new OperationNotPermittedException(
                        "User is not permitted to modify the specified user account.");
                logger.logWarn(Log4jLogger.SYSTEM_LOG | Log4jLogger.AUDIT_LOG, onp,
                        LogMessageIdentifier.WARN_USER_ACCOUNT_MODIFICATION_FAILED,
                        Long.toString(dm.getCurrentUser().getKey()), updatedUser.getUserId());
                throw onp;
            }
        }

        PlatformUser modUser = UserDataAssembler.toPlatformUser(updatedUser);
        modUser.setKey(existingUser.getKey());
        verifyIdUniquenessAndLdapAttributes(existingUser, modUser);

        // now change the user
        UserDataAssembler.updatePlatformUser(updatedUser, existingUser);

        if (sendMail) {
            sendUserUpdatedMail(existingUser, oldUser);
        }

        notifySubscriptionsAboutUserUpdate(existingUser);

        return existingUser;
    }

    @Override
    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public void verifyIdUniquenessAndLdapAttributes(PlatformUser existingUser, PlatformUser modUser)
            throws NonUniqueBusinessKeyException {

        if (!existingUser.getUserId().equals(modUser.getUserId())) {
            final PlatformUser tmpUser = new PlatformUser();
            tmpUser.setKey(modUser.getKey());
            tmpUser.setUserId(modUser.getUserId());
            dm.validateBusinessKeyUniqueness(tmpUser);
        }

        // before performing any changes to the platform user, ensure that none
        // of the LDAP managed attributes will be modified
        UserModificationCheck umc = new UserModificationCheck(ldapSettingsMS.getMappedAttributes());
        umc.check(existingUser, modUser);

    }

    @Override
    @Asynchronous
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void notifySubscriptionsAboutUserUpdate(PlatformUser existingUser) {

        // 2. notify all products the user is subscribed to
        List<Subscription> subscriptions = sm.getSubscriptionsForUserInt(existingUser);
        List<TaskMessage> messages = new ArrayList<>();
        for (Subscription subscription : subscriptions) {
            SubscriptionStatus status = subscription.getStatus();
            // in these states the product instance is not existing
            if (status != SubscriptionStatus.PENDING && status != SubscriptionStatus.INVALID) {
                UsageLicense license = getUsgeLicenseForUserAndSubscription(existingUser, subscription);
                if (license != null) {
                    UpdateUserPayload payload = new UpdateUserPayload(subscription.getKey(), license.getKey());
                    TaskMessage message = new TaskMessage(UpdateUserHandler.class, payload);
                    messages.add(message);
                }
            }
        }
        tqs.sendAllMessages(messages);

    }

    @Override
    @Asynchronous
    public void sendUserUpdatedMail(PlatformUser existingUser, PlatformUser oldUser) {

        // bugfix 8183
        // Send an email to the current organization admin
        List<PlatformUser> platformUsers = new LinkedList<>();
        platformUsers.add(existingUser);

        if (oldUser != null && !oldUser.getEmail().trim().equals(existingUser.getEmail().trim())) {
            platformUsers.add(oldUser);
        }

        SendMailStatus<PlatformUser> mailStatus = cm.sendMail(EmailType.USER_UPDATED, null, null,
                platformUsers.toArray(new PlatformUser[platformUsers.size()]));

        for (SendMailStatusItem<PlatformUser> sendMailStatusItem : mailStatus.getMailStatus()) {
            if (sendMailStatusItem.errorOccurred()) {
                logger.logWarn(Log4jLogger.SYSTEM_LOG, sendMailStatusItem.getException(),
                        LogMessageIdentifier.WARN_MAIL_USER_UPDATE_FAILED);
            }
        }

    }

    @Override
    public void sendAdministratorNotifyMail(PlatformUser administrator, String userId) {
        List<PlatformUser> platformUsers = new LinkedList<>();
        platformUsers.add(administrator);

        SendMailStatus<PlatformUser> mailStatus = cm.sendMail(EmailType.USER_UPDATED_WITH_NOROLE,
                new Object[] { userId }, null, platformUsers.toArray(new PlatformUser[platformUsers.size()]));

        for (SendMailStatusItem<PlatformUser> sendMailStatusItem : mailStatus.getMailStatus()) {
            if (sendMailStatusItem.errorOccurred()) {
                logger.logWarn(Log4jLogger.SYSTEM_LOG, sendMailStatusItem.getException(),
                        LogMessageIdentifier.WARN_MAIL_USER_UPDATE_FAILED);
            }
        }
    }

    @Override
    public void notifyOnLoginAttempt(VOUser user, boolean attemptSuccessful)
            throws ObjectNotFoundException, SecurityCheckException, ValidationException {

        ArgumentValidator.notNull("user", user);

        PlatformUser pUser = dm.getReference(PlatformUser.class, user.getKey());
        if (attemptSuccessful) {
            if (pUser.getStatus().getLockLevel() >= UserAccountStatus.LOCK_LEVEL_LOCKED) {
                SecurityCheckException scf = new SecurityCheckException(
                        "User must not login, as he failed to do so before too many times. Account locked!");
                logger.logWarn(Log4jLogger.SYSTEM_LOG | Log4jLogger.AUDIT_LOG, scf,
                        LogMessageIdentifier.WARN_USER_LOCKED, pUser.getUserId());
                throw scf;
            }
            pUser.setFailedLoginCounter(0);
            if (pUser.getOrganization().isRemoteLdapActive()) {
                syncUserWithLdap(pUser);
            }
        } else {
            int failedAttempts = pUser.getFailedLoginCounter() + 1;
            pUser.setFailedLoginCounter(failedAttempts);
            if (failedAttempts >= getMaxRetryAttempts()) {
                if (pUser.getStatus().getLockLevel() <= UserAccountStatus.LOCKED_FAILED_LOGIN_ATTEMPTS
                        .getLockLevel()) {
                    pUser.setStatus(UserAccountStatus.LOCKED_FAILED_LOGIN_ATTEMPTS);
                }
            }
        }
    }

    @Override
    @RolesAllowed("ORGANIZATION_ADMIN")
    public void unlockUserAccount(VOUser user, String marketplaceId)
            throws ObjectNotFoundException, OperationNotPermittedException, ConcurrentModificationException {

        ArgumentValidator.notNull("user", user);

        // check if user may unlock the account
        PlatformUser pUser = dm.getReference(PlatformUser.class, user.getKey());

        // check whether user belongs to same organization than the caller
        PermissionCheck.sameOrg(dm.getCurrentUser(), pUser, logger);

        ensureNoRemoteLdapUsed(pUser.getOrganization(),
                LogMessageIdentifier.ERROR_LOCK_OPERATION_FORBIDDEN_REMOTE_LDAP);

        BaseAssembler.verifyVersionAndKey(pUser, user);

        if (pUser.getStatus() == UserAccountStatus.PASSWORD_MUST_BE_CHANGED) {
            OperationNotPermittedException onp = new OperationNotPermittedException(
                    "User must change his password at login, unlock not permitted");
            logger.logWarn(Log4jLogger.SYSTEM_LOG | Log4jLogger.AUDIT_LOG, onp,
                    LogMessageIdentifier.WARN_OPERATOR_UNLOCK_FAILED, pUser.getUserId());
            throw onp;
        }

        setUserAccountStatusInternal(pUser, UserAccountStatus.ACTIVE, getMarketplace(marketplaceId));

    }

    @Override
    @RolesAllowed("PLATFORM_OPERATOR")
    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public void setUserAccountStatus(PlatformUser user, UserAccountStatus status)
            throws OrganizationAuthoritiesException {

        // no marketplace passed as this method is only called by the operator
        setUserAccountStatusInternal(user, status, null);

    }

    private void setUserAccountStatusInternal(PlatformUser user, UserAccountStatus status,
            Marketplace marketplace) {

        // update the domain object
        user.setStatus(status);
        if (UserAccountStatus.ACTIVE == status) {
            user.setFailedLoginCounter(0);
        }

        // send e-mail
        try {
            if (UserAccountStatus.ACTIVE == status) {
                cm.sendMail(user, EmailType.USER_UNLOCKED, null, marketplace);
            } else if (UserAccountStatus.LOCKED == status) {
                cm.sendMail(user, EmailType.USER_LOCKED, null, marketplace);
            }
        } catch (MailOperationException e) {
            // do nothing. although the operation failed, the locking of the
            // account must succeed
            logger.logWarn(Log4jLogger.SYSTEM_LOG, LogMessageIdentifier.WARN_SEND_CONFIRMATION_MAIL_FAILED,
                    e.getId());
        }
    }

    @Override
    public VOUser getUser(VOUser user)
            throws ObjectNotFoundException, OperationNotPermittedException, OrganizationRemovedException {

        ArgumentValidator.notNull("user", user);
        PlatformUser pUser;
        try {
            String userId = user.getUserId();
            String orgId = user.getOrganizationId();
            String tenantId = user.getTenantId();

            if (StringUtils.isNotBlank(orgId)) {
                pUser = getPlatformUserByOrganization(userId, orgId);
            } else {
                pUser = getPlatformUser(userId, tenantId, false);
            }

            if (pUser.getOrganization().getDeregistrationDate() != null) {
                OperationNotPermittedException onp = new OperationNotPermittedException(
                        "The user doesn't belong to a active organization.");
                logger.logWarn(Log4jLogger.SYSTEM_LOG | Log4jLogger.ACCESS_LOG, onp,
                        LogMessageIdentifier.WARN_OPERATOR_LOGIN_FAILED, pUser.getUserId());
                throw onp;
            }
        } catch (ObjectNotFoundException e) {
            // user could not be found. so check the history entries, if the
            // organization had been removed because the account was not
            // confirmed.
            // If this holds, throw a OrganizationRemovedException, otherwise
            // pass the ObjectNotFound
            Query query = dm.createNamedQuery("PlatformUserHistory.findUnconfirmedUserForRemovedOrganization");
            query.setParameter("userStatus", UserAccountStatus.LOCKED_NOT_CONFIRMED);
            query.setParameter("modType", ModificationType.DELETE);
            query.setParameter("userId", user.getUserId());
            query.setParameter("organizationId", user.getOrganizationId());
            Object singleResult = null;
            try {
                singleResult = query.getSingleResult();
            } catch (NoResultException nre) {
                // no entry found, so entry never existed. Do nothing to throw
                // the initial ObjectNotFound exception later
            } catch (NonUniqueResultException nue) {
                // should never be caught, indicates inconsistency of data.
                // Throw the object not found exception, but log the problem
                logger.logError(Log4jLogger.SYSTEM_LOG, nue,
                        LogMessageIdentifier.ERROR_NONUNIQUE_ENTRY_FOR_REMOVED_ORGANIZATION);
            }

            if (singleResult != null) {
                // the organization has been removed by the system
                OrganizationRemovedException cre = new OrganizationRemovedException(
                        "Trying to get a user for a organization that was removed by the system", new Object[] {},
                        e);
                logger.logWarn(Log4jLogger.SYSTEM_LOG, cre,
                        LogMessageIdentifier.WARN_USER_REMOVED_ORGANIZATION_FAILED);
                throw cre;
            }

            throw e;

        }

        return UserDataAssembler.toVOUser(pUser);
    }

    @Override
    public VOUserDetails getCurrentUserDetails() {
        return UserDataAssembler.toVOUserDetails(dm.getCurrentUser());
    }

    @Override
    public VOUserDetails getCurrentUserDetailsIfPresent() {
        return UserDataAssembler.toVOUserDetails(dm.getCurrentUserIfPresent());
    }

    @Override
    public void sendAccounts(String email, String marketplaceId)
            throws ValidationException, MailOperationException {

        ArgumentValidator.notNull("email", email);
        BLValidator.isNotBlank("email", email);

        final List<PlatformUser> users = getUsersByEmail(email);
        if (users != null && !users.isEmpty()) {

            String baseUrl = cs.getBaseURL();

            StringBuffer urlBuffer = new StringBuffer();
            urlBuffer.append(baseUrl);
            // remove any trailing slashes from the base url
            removeTrailingSlashes(urlBuffer);
            urlBuffer.append("?oId=");

            String url = urlBuffer.toString();

            StringBuffer text = new StringBuffer();
            for (PlatformUser currentUser : users) {
                String organizationId = currentUser.getOrganization().getOrganizationId();
                text.append("  ").append(organizationId).append(", ");
                text.append(currentUser.getUserId()).append(": ");
                text.append(url);
                try {
                    text.append(URLEncoder.encode(organizationId, "UTF-8"));
                    if (marketplaceId != null && marketplaceId.trim().length() > 0) {
                        text.append("&mId=");
                        text.append(URLEncoder.encode(marketplaceId, "UTF-8"));
                    }
                    text.append("\n");
                } catch (UnsupportedEncodingException e) {
                    logger.logError(Log4jLogger.SYSTEM_LOG, e, LogMessageIdentifier.ERROR_ENCODE_URL_FAILED,
                            currentUser.getOrganization().getOrganizationId());
                }
            }
            cm.sendMail(users.get(0), EmailType.USER_ACCOUNTS, new Object[] { text.toString() },
                    getMarketplace(marketplaceId));
        }

    }

    @Override
    @Interceptors({ ServiceProviderInterceptor.class })
    public List<VOUserDetails> searchLdapUsers(final String userIdPattern) throws ValidationException {

        ArgumentValidator.notNull("userIdPattern", userIdPattern);

        Organization organization = dm.getCurrentUser().getOrganization();

        LdapConnector connector = getLdapConnectionForOrganization(organization);
        Properties dirProperties = connector.getDirProperties();
        Map<SettingType, String> attrMap = connector.getAttrMap();
        String baseDN = connector.getBaseDN();

        List<SettingType> attrList = new ArrayList<>(attrMap.keySet());
        ILdapResultMapper<VOUserDetails> mapper = new LdapVOUserDetailsMapper(null, attrMap);
        try {
            // read user from LDAP
            List<VOUserDetails> voUserList = ldapAccess.search(dirProperties, baseDN,
                    getLdapSearchFilter(attrMap, userIdPattern), mapper, false);

            int size = voUserList.size();
            for (int i = 0; i < size; i++) {
                VOUserDetails voUser = voUserList.get(i);
                PlatformUser user = getPlatformUserByOrgAndReamUserId(organization, voUser.getRealmUserId());
                if (null != user) {
                    // update the domain object with possibly changed LDAP
                    // attributes and return a complete value object
                    UserDataAssembler.updatePlatformUser(voUser, attrList, user);
                    voUserList.set(i, UserDataAssembler.toVOUserDetails(user));
                } else {
                    // set some mandatory attributes
                    voUser.setOrganizationId(organization.getOrganizationId());
                    String locale = voUser.getLocale();
                    if (locale == null || locale.trim().length() == 0) {
                        voUser.setLocale(organization.getLocale());
                    }
                }
            }
            return voUserList;
        } catch (NamingException e) {
            Object[] params = new Object[] { dirProperties.get(Context.PROVIDER_URL), e.getMessage() };
            ValidationException vf = new ValidationException(ReasonEnum.LDAP_CONNECTION_REFUSED, null, params);
            logger.logError(Log4jLogger.SYSTEM_LOG, vf, LogMessageIdentifier.ERROR_LDAP_SYSTEM_CONNECTION_REFUSED);
            throw vf;
        }
    }

    @Override
    @Interceptors({ ServiceProviderInterceptor.class })
    public boolean searchLdapUsersOverLimit(final String userIdPattern) throws ValidationException {
        ArgumentValidator.notNull("userIdPattern", userIdPattern);

        Organization organization = dm.getCurrentUser().getOrganization();

        LdapConnector connector = getLdapConnectionForOrganization(organization);
        Properties dirProperties = connector.getDirProperties();
        Map<SettingType, String> attrMap = connector.getAttrMap();
        String baseDN = connector.getBaseDN();
        ILdapResultMapper<VOUserDetails> mapper = new LdapVOUserDetailsMapper(null, attrMap);
        try {
            return ldapAccess.searchOverLimit(dirProperties, baseDN, getLdapSearchFilter(attrMap, userIdPattern),
                    mapper, false);
        } catch (NamingException e) {
            Object[] params = new Object[] { dirProperties.get(Context.PROVIDER_URL), e.getMessage() };
            ValidationException vf = new ValidationException(ReasonEnum.LDAP_CONNECTION_REFUSED, null, params);
            logger.logError(Log4jLogger.SYSTEM_LOG, vf, LogMessageIdentifier.ERROR_LDAP_SYSTEM_CONNECTION_REFUSED);
            throw vf;
        }
    }

    private PlatformUser getPlatformUserByOrgAndReamUserId(Organization org, String realmUserId) {
        Query query = dm.createNamedQuery("PlatformUser.findByOrgAndReamUserId");
        query.setParameter("organization", org);
        query.setParameter("realmUserId", realmUserId);
        try {
            return (PlatformUser) query.getSingleResult();
        } catch (Exception e) {
            return null;
        }
    }

    @Override
    @Interceptors({ ServiceProviderInterceptor.class })
    public void importLdapUsers(List<VOUserDetails> users, String marketplaceId)
            throws NonUniqueBusinessKeyException, ValidationException, MailOperationException {

        ArgumentValidator.notNull("users", users);

        Organization organization = dm.getCurrentUser().getOrganization();

        LdapConnector connector = getLdapConnectionForOrganization(organization);
        Properties dirProperties = connector.getDirProperties();
        Map<SettingType, String> attrMap = connector.getAttrMap();
        String baseDN = connector.getBaseDN();

        Marketplace marketplace = getMarketplace(marketplaceId);
        for (VOUserDetails user : users) {
            try {
                ILdapResultMapper<VOUserDetails> mapper = new LdapVOUserDetailsMapper(user, attrMap);
                List<VOUserDetails> list = ldapAccess.search(dirProperties, baseDN,
                        getLdapSearchFilter(attrMap, user.getRealmUserId()), mapper, false);
                int size = list.size();
                if (size == 1) {
                    user = list.get(0);
                    if (GenericValidator.isBlankOrNull(user.getLocale())) {
                        user.setLocale(organization.getLocale());
                    }
                    try {
                        addPlatformUser(user, organization, null, UserAccountStatus.ACTIVE, true, false,
                                marketplace, false);
                    } catch (UserRoleAssignmentException e) {
                        sessionCtx.setRollbackOnly();
                        ValidationException vf = new ValidationException(e.getMessage());
                        logger.logError(Log4jLogger.SYSTEM_LOG, vf,
                                LogMessageIdentifier.ERROR_VALIDATION_PARAMETER_LDAP_FOUND_ERROR, "User");
                        throw vf;
                    }

                } else if (size == 0) {
                    sessionCtx.setRollbackOnly();
                    ValidationException vf = new ValidationException(ReasonEnum.LDAP_USER_NOT_FOUND, null,
                            new Object[] { user.getRealmUserId() });
                    logger.logError(Log4jLogger.SYSTEM_LOG, vf,
                            LogMessageIdentifier.ERROR_VALIDATION_PARAMETER_LDAP_FOUND_ERROR, "User");
                    throw vf;
                } else {
                    sessionCtx.setRollbackOnly();
                    ValidationException vf = new ValidationException(ReasonEnum.LDAP_USER_NOT_UNIQUE, null,
                            new Object[] { user.getRealmUserId() });
                    logger.logError(Log4jLogger.SYSTEM_LOG, vf,
                            LogMessageIdentifier.ERROR_VALIDATION_PARAMETER_LDAP_FOUND_ERROR, "User");
                    throw vf;
                }
            } catch (NamingException e) {
                Object[] params = new Object[] { dirProperties.get(Context.PROVIDER_URL), e.getMessage() };
                ValidationException vf = new ValidationException(ReasonEnum.LDAP_CONNECTION_REFUSED, null, params);
                logger.logError(Log4jLogger.SYSTEM_LOG, vf,
                        LogMessageIdentifier.ERROR_LDAP_SYSTEM_CONNECTION_REFUSED);
                throw vf;
            }
        }
    }

    @Override
    @RolesAllowed({ "SERVICE_MANAGER", "TECHNOLOGY_MANAGER" })
    public VOUserDetails createOnBehalfUser(String organizationId, String password)
            throws ObjectNotFoundException, OperationNotPermittedException, NonUniqueBusinessKeyException {

        PlatformUser currentUser = dm.getCurrentUser();

        Organization customer = validateForOnBehalfUserCreation(organizationId, password, currentUser);
        ensureNoRemoteLdapUsed(customer, LogMessageIdentifier.ERROR_ON_BEHALF_USER_OPERATION_REMOTE_LDAP);
        PlatformUser customerUser = createOnBehalfUser(password, currentUser, customer);
        dm.refresh(customerUser);
        grantMaxUserRoles(customerUser);
        dm.flush();
        dm.refresh(customerUser);

        return UserDataAssembler.toVOUserDetails(customerUser);
    }

    /**
     * Grants the max set of roles to the user as allowed by his organization.
     * 
     * @param customerUser
     *            The user to grant the roles to.
     */
    private void grantMaxUserRoles(PlatformUser customerUser) {
        grantRole(customerUser, UserRoleType.ORGANIZATION_ADMIN);
        if (customerUser.getOrganization().hasRole(OrganizationRoleType.TECHNOLOGY_PROVIDER)) {
            grantRole(customerUser, UserRoleType.TECHNOLOGY_MANAGER);
        }
        if (customerUser.getOrganization().hasRole(OrganizationRoleType.SUPPLIER)) {
            grantRole(customerUser, UserRoleType.SERVICE_MANAGER);
        }
    }

    @Override
    public void cleanUpCurrentUser() {

        OnBehalfUserReference onBehalf = dm.getCurrentUser().getMaster();
        if (onBehalf != null) {
            dm.remove(onBehalf);
        }

    }

    @Override
    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public void createOrganizationAdmin(VOUserDetails userDetails, Organization organization, String password,
            Long serviceKey, Marketplace marketplace) throws NonUniqueBusinessKeyException, ObjectNotFoundException,
            ValidationException, MailOperationException {
        ArgumentValidator.notNull("organization", organization);
        ArgumentValidator.notNull("userDetails", userDetails);

        PlatformUser organizationAdmin = null;

        // 1. create the user with the given password or with a generated one,
        // if none was set
        String pwd = password;
        boolean isAutoGeneratedPassword = false;
        if (password == null) {
            PasswordGenerator gen = new PasswordGenerator();
            pwd = gen.generatePassword();
            isAutoGeneratedPassword = true;
        }

        try {
            if (organization.isRemoteLdapActive()) {
                organizationAdmin = addPlatformUser(userDetails, organization, pwd, UserAccountStatus.ACTIVE, true,
                        false, marketplace, false, true);

            } else {
                if (isAutoGeneratedPassword) {
                    organizationAdmin = addPlatformUser(userDetails, organization, pwd,
                            UserAccountStatus.PASSWORD_MUST_BE_CHANGED, true, true, marketplace, false, true);
                } else {
                    organizationAdmin = addPlatformUser(userDetails, organization, pwd,
                            UserAccountStatus.LOCKED_NOT_CONFIRMED, false, true, marketplace, false, true);
                }
            }
        } catch (UserRoleAssignmentException e) {
            throw new ValidationException(e.getMessage());
        }

        if (marketplace == null) {
            checkMinimumUserRoleConstrains(organizationAdmin);
        }

        // 2. send an email to the user to indicate the need to confirm the
        // account. Only required, if a self-chosen password is used
        if (!isAutoGeneratedPassword) {
            String marketplaceId = null;
            if (marketplace != null) {
                marketplaceId = marketplace.getMarketplaceId();
            }
            StringBuffer url = new StringBuffer();

            url.append(cs.getBaseURL());

            // remove any trailing slashes from the base url
            removeTrailingSlashes(url);

            String[] urlParam = new String[4];
            urlParam[0] = organizationAdmin.getOrganization().getOrganizationId();
            urlParam[1] = organizationAdmin.getUserId();
            urlParam[2] = marketplaceId;
            urlParam[3] = serviceKey == null ? null : String.valueOf(serviceKey.longValue());

            if (!organizationAdmin.hasManagerRole()) {
                url.append("/marketplace/confirm.jsf?")
                        .append((marketplaceId != null) ? "mId=" + marketplaceId + "&" : "").append("enc=");
            } else {
                url.append("/public/confirm.jsf?")
                        .append((marketplaceId != null) ? "mId=" + marketplaceId + "&" : "").append("enc=");
            }

            try {
                url.append(URLEncoder.encode(ParameterEncoder.encodeParameters(urlParam), "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                MailOperationException mof = new MailOperationException("Confirmation URL creation failed!", e);
                logger.logError(Log4jLogger.SYSTEM_LOG, mof, LogMessageIdentifier.ERROR_UNSUPPORTED_ENCODING);
                throw mof;
            }

            // Bug 7865: Add a dummy postfix to avoid problems with the
            // automatic link recognition in outlook.
            url.append("&et");

            String confirmationURL = url.toString();
            cm.sendMail(organizationAdmin, EmailType.USER_CONFIRM, new Object[] { confirmationURL }, marketplace);
        }

    }

    @Override
    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public void deletePlatformUser(PlatformUser user, Marketplace marketplace)
            throws UserDeletionConstraintException, ObjectNotFoundException, TechnicalServiceNotAliveException,
            TechnicalServiceOperationException {
        dm.getReference(PlatformUser.class, user.getKey());
        // internal call, so also admin users may be revoked
        deletePlatformUser(user, true, true, marketplace);
    }

    /**
     * Method has been deprecated. Use #{@getPlatformUser}
     * with tenant parmeter.
     * 
     * @param userId
     *            The user identifying attributes' representation.
     * @param validateOrganization
     *            <code>true</code> if the calling user must be part of the same
     *            organization as the requested user.
     * @return
     * @throws ObjectNotFoundException
     * @throws OperationNotPermittedException
     */
    @Deprecated
    @Override
    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public PlatformUser getPlatformUser(String userId, boolean validateOrganization)
            throws ObjectNotFoundException, OperationNotPermittedException {

        return getPlatformUser(userId, null, validateOrganization);
    }

    @Override
    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public PlatformUser getPlatformUser(String userId, String tenantId, boolean validateOrganization)
            throws ObjectNotFoundException, OperationNotPermittedException {

        PlatformUser platformUser = new PlatformUser();
        platformUser.setUserId(userId);
        platformUser.setTenantId(tenantId);
        platformUser = dm.find(platformUser);

        if (platformUser == null) {
            ObjectNotFoundException onf = new ObjectNotFoundException(ObjectNotFoundException.ClassEnum.USER,
                    userId);
            logger.logWarn(Log4jLogger.SYSTEM_LOG, onf, LogMessageIdentifier.WARN_USER_NOT_FOUND);
            throw onf;
        }

        if (validateOrganization) {
            // Validate whether the calling user belongs to the same
            // organization as the requested user. Otherwise an exception will
            // be thrown.
            PermissionCheck.sameOrg(dm.getCurrentUser(), platformUser, logger);
        }

        return platformUser;
    }

    @Override
    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public List<PlatformUser> getOverdueOrganizationAdmins(long currentTime) {
        String period = cs.getConfigurationSetting(ConfigurationKey.PERMITTED_PERIOD_UNCONFIRMED_ORGANIZATIONS,
                Configuration.GLOBAL_CONTEXT).getValue();
        long maxTime = currentTime - Long.parseLong(period);

        Query query = dm.createNamedQuery("PlatformUser.getOverdueOrganizationAdmins");
        query.setParameter("status", UserAccountStatus.LOCKED_NOT_CONFIRMED);
        query.setParameter("date", Long.valueOf(maxTime));
        return ParameterizedTypes.list(query.getResultList(), PlatformUser.class);
    }

    //
    // internal methods, not considering txn at all
    //

    void deletePlatformUser(PlatformUser userToBeDeleted, boolean deleteLastAdmin, boolean selfDeletionAllowed,
            Marketplace marketplace) throws UserDeletionConstraintException, TechnicalServiceNotAliveException,
            TechnicalServiceOperationException {

        // 1a. check if the user belongs to any subscription. If so, he must not
        // be deleted
        List<Subscription> subscriptions = sm.getSubscriptionsForUserInt(userToBeDeleted);
        boolean ifAllExpired = isAllSubscriptionExpired(subscriptions);
        if (subscriptions.size() > 0 && !ifAllExpired) {
            UserDeletionConstraintException udcv = new UserDeletionConstraintException(
                    "User '" + userToBeDeleted.getUserId()
                            + "' is registered for a subscription and cannot be deleted!",
                    Reason.HAS_ACTIVE_SUBSCRIPTIONS);
            logger.logWarn(Log4jLogger.SYSTEM_LOG, udcv, LogMessageIdentifier.WARN_USER_DELETION_FAILED);
            throw udcv;
        }
        if (isUserLoggedIn(userToBeDeleted.getKey())) {
            UserDeletionConstraintException udcv = new UserDeletionConstraintException(
                    "User '" + userToBeDeleted.getUserId() + "' is logged in and cannot be deleted!",
                    Reason.IS_USER_LOGGED_IN);
            logger.logWarn(Log4jLogger.SYSTEM_LOG, udcv, LogMessageIdentifier.WARN_USER_DELETION_FAILED);
            throw udcv;
        }
        if (!selfDeletionAllowed) {
            if (sessionCtx.getCallerPrincipal().getName().equals(String.valueOf(userToBeDeleted.getKey()))) {
                UserDeletionConstraintException udcv = new UserDeletionConstraintException(
                        "User '" + userToBeDeleted.getUserId() + "' cannot delete himself!",
                        Reason.FORBIDDEN_SELF_DELETION);
                logger.logWarn(Log4jLogger.SYSTEM_LOG, udcv, LogMessageIdentifier.WARN_USER_DELETION_FAILED);
                throw udcv;
            }
        }
        if (!deleteLastAdmin) {
            if (isUserLastAdminForOrganization(userToBeDeleted)) {
                UserDeletionConstraintException udcv = new UserDeletionConstraintException(
                        "User '" + userToBeDeleted.getUserId()
                                + "' cannot be deleted as he is the last admin for the organization.",
                        Reason.LAST_ADMIN);
                logger.logWarn(Log4jLogger.SYSTEM_LOG, udcv, LogMessageIdentifier.WARN_USER_DELETION_FAILED);
                throw udcv;
            }
        }

        // It's needed to delete review data before user is deleted
        try {
            if (subscriptions.size() > 0 && ifAllExpired) {
                for (Subscription subscription : subscriptions) {
                    sm.revokeUserFromSubscriptionInt(subscription, Collections.singletonList(userToBeDeleted));
                }
            }
            rvs.deleteReviewsOfUser(userToBeDeleted, false);
        } catch (OperationNotPermittedException e) {
            // permission check of delete review is off, so this exception must
            // not happen
            SaaSSystemException sse = new SaaSSystemException(e);
            logger.logError(Log4jLogger.SYSTEM_LOG, sse,
                    LogMessageIdentifier.ERROR_DELETE_USER_FAILED_BY_DELETION_REVIEW_PERMISSION_ERROR);
            throw sse;
        }

        // 2. as all constraints are met, remove the user from the database
        dm.remove(userToBeDeleted);

        // 3. send email to inform the user about the deleted account
        try {
            String organizationName = userToBeDeleted.getOrganization().getName();
            cm.sendMail(userToBeDeleted, EmailType.USER_DELETED,
                    new Object[] { organizationName == null ? "" : organizationName }, marketplace);
        } catch (MailOperationException e) {
            logger.logWarn(Log4jLogger.SYSTEM_LOG, LogMessageIdentifier.WARN_SEND_USER_DELETED_MAIL_FAILED);
        }

    }

    PlatformUser addPlatformUser(VOUserDetails userDetails, Organization organization, String password,
            UserAccountStatus lockLevel, boolean sendMail, boolean userLocalLdap, Marketplace marketplace,
            boolean performRoleCheck) throws NonUniqueBusinessKeyException, MailOperationException,
            ValidationException, UserRoleAssignmentException {
        return addPlatformUser(userDetails, organization, password, lockLevel, sendMail, userLocalLdap, marketplace,
                performRoleCheck, false);
    }

    // TODO: platform user persisting
    private PlatformUser addPlatformUser(VOUserDetails userDetails, Organization organization, String password,
            UserAccountStatus lockLevel, boolean sendMail, boolean userLocalLdap, Marketplace marketplace,
            boolean performRoleCheck, boolean createOrgAdminRole) throws NonUniqueBusinessKeyException,
            MailOperationException, ValidationException, UserRoleAssignmentException {

        if (!userLocalLdap) {
            // Remote LDAP active
            if (!isUserIdUnique(userDetails.getRealmUserId())) {
                // The user name in the LDAP system is not unique in the
                // context of BES, a new unique user name need to be created for
                // BES usage.
                String uniqueId = createUniqueUserId(userDetails);
                try {
                    BLValidator.isUserId("uniqueId", uniqueId, true);
                } catch (ValidationException e) {
                    ValidationException vf = new ValidationException(ReasonEnum.LDAP_CREATED_ID_INVALID, null,
                            new Object[] { uniqueId });
                    logger.logError(Log4jLogger.SYSTEM_LOG, vf,
                            LogMessageIdentifier.ERROR_VALIDATION_PARAMETER_LDAP_FOUND_ERROR, "User ID");
                    throw vf;
                }
                userDetails.setUserId(uniqueId);
            } else {
                userDetails.setUserId(userDetails.getRealmUserId());
            }
        }
        PlatformUser pu = UserDataAssembler.toPlatformUser(userDetails);

        pu.setOrganization(organization);
        if (userLocalLdap) {
            // Do not set the user password if CT-MG is a SAML SP
            if (!cs.isServiceProvider()) {
                setPassword(pu, password);
            }
        }
        pu.setCreationDate(DateFactory.getInstance().getTransactionDate());
        if (userDetails.getRealmUserId() != null) {
            pu.setRealmUserId(userDetails.getRealmUserId());
        } else {
            // If the realm id is not available, use the normal BES user id
            // instead
            pu.setRealmUserId(userDetails.getUserId());
        }

        // Set the user account status to active if CT-MG is a SAML SP
        if (cs.isServiceProvider() && UserAccountStatus.PASSWORD_MUST_BE_CHANGED.equals(lockLevel)) {
            pu.setStatus(UserAccountStatus.ACTIVE);
        } else {
            pu.setStatus(lockLevel);
        }
        dm.persist(pu);

        for (UserRoleType role : userDetails.getUserRoles()) {
            if (!pu.hasRole(role)) {
                if (performRoleCheck) {
                    checkRoleConstrains(pu, role);
                }
                grantRole(pu, role);
            }
        }

        if (createOrgAdminRole && (!userDetails.hasAdminRole())) {
            grantRole(pu, UserRoleType.ORGANIZATION_ADMIN);
        }

        if (performRoleCheck && marketplace == null) {
            checkMinimumUserRoleConstrains(pu);
        }

        dm.flush();
        dm.refresh(organization);
        dm.refresh(pu);

        if (sendMail) {
            sendMailToCreatedUser(password, userLocalLdap, marketplace, pu);
        }
        return pu;
    }

    UserGroup getDefaultUserGroupForOrganization(Organization org) {
        ArgumentValidator.notNull("organization", org);

        for (UserGroup group : org.getUserGroups()) {
            if (group.isDefault()) {
                return group;
            }
        }
        return null;
    }

    @Override
    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public void sendMailToCreatedUser(String password, boolean userLocalLdap, Marketplace marketplace,
            PlatformUser pu) throws MailOperationException {
        String tenantId = getTenantIdForEmail(pu);
        if (!SendMailControl.isSendMail()) {
            // keep password for later sending
            SendMailControl.setMailData(password, marketplace);
            return;
        }
        String marketplaceId = null;
        if (marketplace != null) {
            marketplaceId = marketplace.getMarketplaceId();
        }

        if (userLocalLdap) {
            // if the user role is manager and the marketplaceId exists, which
            // happens only by registering users as a supplier in the
            // marketplace portal, then append both the base url and the
            // marketplace url.
            if (pu.hasManagerRole()) {
                if (marketplaceId != null) {
                    if (cs.isServiceProvider()) {
                        cm.sendMail(
                                pu, EmailType.USER_CREATED_WITH_MARKETPLACE_SAML_SP, new Object[] { pu.getUserId(),
                                        cm.getBaseUrlWithTenant(tenantId), cm.getMarketplaceUrl(marketplaceId) },
                                marketplace);

                    } else {
                        cm.sendMail(pu, EmailType.USER_CREATED_WITH_MARKETPLACE,
                                new Object[] { pu.getUserId(), password, cm.getBaseUrl(),
                                        cm.getMarketplaceUrl(marketplaceId), String.valueOf(pu.getKey()) },
                                marketplace);

                    }
                } else {
                    if (cs.isServiceProvider()) {
                        cm.sendMail(pu, EmailType.USER_CREATED_SAML_SP,
                                new Object[] { pu.getUserId(), cm.getBaseUrlWithTenant(tenantId) }, marketplace);
                    } else {
                        cm.sendMail(pu, EmailType.USER_CREATED, new Object[] { pu.getUserId(), password,
                                cm.getBaseUrl(), String.valueOf(pu.getKey()) }, marketplace);

                    }
                }

            } else {
                if (cs.isServiceProvider()) {
                    cm.sendMail(pu, EmailType.USER_CREATED_SAML_SP,
                            new Object[] { pu.getUserId(), cm.getMarketplaceUrl(marketplaceId) }, marketplace);
                } else {
                    cm.sendMail(
                            pu, EmailType.USER_CREATED, new Object[] { pu.getUserId(), password,
                                    cm.getMarketplaceUrl(marketplaceId), String.valueOf(pu.getKey()) },
                            marketplace);
                }

            }
        } else {
            if (pu.hasManagerRole()) {
                if (marketplaceId != null) {
                    cm.sendMail(pu, EmailType.USER_IMPORTED_WITH_MARKETPLACE, new Object[] { pu.getUserId(), "",
                            cm.getBaseUrl(), cm.getMarketplaceUrl(marketplaceId), String.valueOf(pu.getKey()) },
                            marketplace);

                } else {
                    cm.sendMail(pu, EmailType.USER_IMPORTED,
                            new Object[] { pu.getUserId(), "", cm.getBaseUrl(), String.valueOf(pu.getKey()) },
                            marketplace);

                }
            } else {
                cm.sendMail(pu, EmailType.USER_IMPORTED, new Object[] { pu.getUserId(), "",
                        cm.getMarketplaceUrl(marketplaceId), String.valueOf(pu.getKey()) }, marketplace);
            }
        }
    }

    private String getTenantIdForEmail(PlatformUser user) {
        final Tenant tenant = user.getOrganization().getTenant();
        String tenantId = null;
        if (tenant != null) {
            tenantId = tenant.getTenantId();
        }
        return tenantId;
    }

    /**
     * 
     * Returns the configured number of retry attempts. If none is specified,
     * the fallback value will be returned.
     * 
     * @return The number of max. allowed login retry attempts.
     */
    private int getMaxRetryAttempts() {
        return Integer.parseInt(
                cs.getConfigurationSetting(ConfigurationKey.MAX_NUMBER_LOGIN_ATTEMPTS, Configuration.GLOBAL_CONTEXT)
                        .getValue());
    }

    private void createUserRoleRelationInt(PlatformUser user, UserRoleType role)
            throws UserRoleAssignmentException {
        if (!user.hasRole(role)) {
            checkRoleConstrains(user, role);
            grantRole(user, role);
        }
    }

    @Override
    @RolesAllowed("ORGANIZATION_ADMIN")
    public void grantUnitRole(PlatformUser user, UserRoleType role) {
        if (role.isUnitRole()) {
            grantRole(user, role);
        }
    }

    @Override
    @RolesAllowed("ORGANIZATION_ADMIN")
    public void revokeUnitRole(PlatformUser user, UserRoleType role) {
        if (!user.hasRole(role)) {
            return;
        }
        if (!role.isUnitRole()) {
            return;
        }
        RoleAssignment roleAssignment = user.getAssignedRole(role);
        if (roleAssignment != null) {
            dm.remove(roleAssignment);
        }
    }

    void checkMinimumUserRoleConstrains(PlatformUser user) throws ValidationException {
        if (user.getKey() > 0) {
            dm.flush();
            dm.refresh(user);
        }
        dm.refresh(user.getOrganization());
        final Set<OrganizationRoleType> orgRoles = user.getOrganization().getGrantedRoleTypes();
        if (orgRoles.isEmpty()) {
            final ValidationException ve = new ValidationException(
                    "Organization must have at least one role, before creating a user. OrgId: "
                            + user.getOrganization().getOrganizationId());
            logger.logWarn(Log4jLogger.SYSTEM_LOG | Log4jLogger.AUDIT_LOG, ve,
                    LogMessageIdentifier.ERROR_USER_CREATION_FAILED_WITH_VALIDATION_ERROR);
            throw ve;
        }
        if (orgRoles.size() == 1 && orgRoles.iterator().next() == OrganizationRoleType.CUSTOMER) {
            ValidationException ve = new ValidationException(ReasonEnum.CUSTOMER_CREATION_ONLY_ON_MARKETPLACE, null,
                    null);
            logger.logWarn(Log4jLogger.SYSTEM_LOG | Log4jLogger.AUDIT_LOG, ve,
                    LogMessageIdentifier.ERROR_USER_CREATION_FAILED_WITH_VALIDATION_ERROR);
            throw ve;
        }
        final List<UserRoleType> requiredUserRoles = new ArrayList<>();
        for (OrganizationRoleType orgRole : orgRoles) {
            final UserRoleType userRole = orgRole.correspondingUserRole();
            if (userRole != null) {
                if (user.hasRole(userRole) || user.hasRole(UserRoleType.ORGANIZATION_ADMIN)) {
                    return;
                }
                requiredUserRoles.add(userRole);
            }
        }

        final String[] s = new String[requiredUserRoles.size()];
        for (int i = 0; i < requiredUserRoles.size(); i++) {
            s[i] = requiredUserRoles.get(i).name();
        }

        final StringBuffer orgRoleString = new StringBuffer();
        for (OrganizationRoleType orgRole : orgRoles) {
            orgRoleString.append(" and ").append(orgRole.name());
        }
        orgRoleString.delete(0, 5);

        final StringBuffer userRoleString = new StringBuffer();
        for (UserRoleType userRole : requiredUserRoles) {
            userRoleString.append(" or ").append(userRole.name());
        }
        userRoleString.delete(0, 4);

        final ValidationException ve = new ValidationException(ReasonEnum.ROLE_REQUIRED, null, s);
        logger.logWarn(Log4jLogger.SYSTEM_LOG | Log4jLogger.AUDIT_LOG, ve,
                LogMessageIdentifier.WARN_USER_ROLE_REQUIRED, user.getUserId(), userRoleString.toString());
        throw ve;
    }

    void checkRoleConstrains(PlatformUser user, UserRoleType role) throws UserRoleAssignmentException {
        if (!dm.getCurrentUser().hasRole(UserRoleType.ORGANIZATION_ADMIN)
                && !dm.getCurrentUser().hasRole(UserRoleType.PLATFORM_OPERATOR)) {
            String msg = String.format("Role assign/revoke violation. Only %s and %s can assign/revoke roles.",
                    UserRoleType.ORGANIZATION_ADMIN, UserRoleType.PLATFORM_OPERATOR);
            UserRoleAssignmentException ure = new UserRoleAssignmentException(msg);
            logger.logWarn(Log4jLogger.SYSTEM_LOG | Log4jLogger.AUDIT_LOG, ure,
                    LogMessageIdentifier.WARN_USER_ROLE_REQUIRED, user.getUserId());
            throw ure;
        }
        if (!isAllowedUserRole(user.getOrganization(), role) && !role.isUnitRole()) {
            OrganizationRoleType orgRole = OrganizationRoleType.correspondingOrgRoleForUserRole(role);
            String msg = String.format(
                    "Role constraint violation. User role '%s' can only be assigned/revoked to %s.", role.name(),
                    orgRole.name());
            UserRoleAssignmentException ure = new UserRoleAssignmentException(msg,
                    new Object[] { orgRole.name(), role.name() });
            logger.logWarn(Log4jLogger.SYSTEM_LOG | Log4jLogger.AUDIT_LOG, ure,
                    LogMessageIdentifier.WARN_USER_ROLE_REQUIRED, user.getUserId(), orgRole.name());
            throw ure;
        }
    }

    void grantRole(PlatformUser user, UserRoleType userRole) {
        if (user.hasRole(userRole)) {
            return;
        }
        UserRole uRole = (UserRole) dm.find(new UserRole(userRole));
        if (uRole == null) {
            throw new SaaSSystemException(
                    "UserRole " + userRole + " not found. This object is created by the SQL setup scripts.");
        }
        RoleAssignment assignment = new RoleAssignment();
        assignment.setRole(uRole);
        assignment.setUser(user);
        try {
            dm.persist(assignment);
        } catch (NonUniqueBusinessKeyException ignore) {
            // user has role already
        }
    }

    void revokeRole(PlatformUser user, UserRoleType userRole) throws UserModificationConstraintException {

        if (userRole == UserRoleType.ORGANIZATION_ADMIN) {

            if (isUserLastAdminForOrganization(user)) {
                UserModificationConstraintException e = new UserModificationConstraintException(
                        UserModificationConstraintException.Reason.LAST_ADMIN);
                logger.logWarn(Log4jLogger.SYSTEM_LOG, e, LogMessageIdentifier.WARN_REVOKE_USER_ROLE_FAILED,
                        String.valueOf(UserRoleType.ORGANIZATION_ADMIN));
                throw e;
            }
        }
        RoleAssignment roleAssignment = user.getAssignedRole(userRole);
        if (roleAssignment != null) {
            dm.remove(roleAssignment);
        }
    }

    /**
     * Checks if the given user is the last administrative user for the
     * organization.
     * 
     * @param userToBeDeleted
     *            The user the check is performed for
     * @return <code>true</code>in case the user is the last administrative user
     *         for the organization, <code>false</code> otherwise.
     */
    private boolean isUserLastAdminForOrganization(PlatformUser userToBeDeleted) {
        if (userToBeDeleted.isOrganizationAdmin()) {
            List<VOUserDetails> usersForOrganization = getUsersForOrganization();
            int count = 0;
            for (VOUserDetails currentUser : usersForOrganization) {
                if (currentUser.hasAdminRole()) {
                    count++;
                }
            }
            if (count <= 1) {
                return true;
            }
        }
        return false;
    }

    /**
     * Retrieves all users with the given email (the search is performed case
     * insensitive).
     * 
     * @param email
     *            the email for the search
     * @return A list of registered users.
     */
    private List<PlatformUser> getUsersByEmail(String email) {
        Query query = dm.createNamedQuery("PlatformUser.listByEmail");
        query.setParameter("email", email);
        return ParameterizedTypes.list(query.getResultList(), PlatformUser.class);
    }

    private LdapConnector getLdapConnectionForOrganization(Organization organization) throws ValidationException {
        Properties resolvedProps = null;
        try {
            resolvedProps = ldapSettingsMS.getOrganizationSettingsResolved(organization.getOrganizationId());
        } catch (ObjectNotFoundException e) {
            // must not happen cause method can only be invoked by platform
            // operator
            SaaSSystemException sse = new SaaSSystemException(e);
            logger.logError(Log4jLogger.SYSTEM_LOG, sse, LogMessageIdentifier.ERROR_UNKNOWN_ORGANIZATION,
                    organization.getOrganizationId());
            throw sse;
        }
        LdapConnector connector = getLdapConnector(resolvedProps);
        connector.ensureAllMandatoryLdapPropertiesPresent();
        return connector;
    }

    LdapConnector getLdapConnector(Properties resolvedProps) {
        return new LdapConnector(ldapAccess, resolvedProps);
    }

    /**
     * Create the LDAP search filter an return it.
     * 
     * @param attrMap
     *            the map with all LDAP attribute setting types.
     * @param pattern
     *            the pattern for the search.
     * @return the search filter
     */
    private String getLdapSearchFilter(Map<SettingType, String> attrMap, String pattern) {
        String uid = attrMap.get(SettingType.LDAP_ATTR_UID);
        if (uid == null) {
            uid = SettingType.LDAP_ATTR_UID.getDefaultValue();
        }
        return uid += "=" + pattern;
    }

    /**
     * Creates and persists a new platform user using the settings of the
     * current user, except the reference to the organization and the user
     * identifier. The user will be created as administrative user.
     * 
     * @param password
     *            The password to be assigned to the user. In case the
     *            authentication mode is SAML_SP, the password parameter is used
     *            as the userId to be set for the created user.
     * 
     * @param currentUser
     *            The master user for the user to be created.
     * @param customer
     *            The customer organization.
     * 
     * @return
     * @throws NonUniqueBusinessKeyException
     */
    private PlatformUser createOnBehalfUser(String password, PlatformUser currentUser, Organization customer)
            throws NonUniqueBusinessKeyException {
        VOUserDetails voUserDetails = UserDataAssembler.toVOUserDetails(currentUser);
        voUserDetails.setOrganizationId(customer.getOrganizationId());
        PlatformUser customerUser = null;
        int retryCount = 0;
        boolean isSAML = cs.isServiceProvider();
        while (customerUser == null) {
            try {
                // If the authentication mode is in SAML_SP, no automatic user
                // id
                // must be generated, as the user must already be present in the
                // IdP.
                // Instead, the password parameter is used as the userId.
                if (isSAML) {
                    voUserDetails.setUserId(password);
                } else {
                    voUserDetails.setUserId(IdGenerator.generateArtificialIdentifier());
                }
                customerUser = addPlatformUser(voUserDetails, customer, password, UserAccountStatus.ACTIVE, false,
                        true, null, false);
                OnBehalfUserReference onBehalf = new OnBehalfUserReference();
                onBehalf.setMasterUser(currentUser);
                onBehalf.setSlaveUser(customerUser);
                dm.persist(onBehalf);
            } catch (NonUniqueBusinessKeyException e) {
                if (isSAML) {
                    logger.logDebug("User with the userId " + password
                            + " already exists. But this is not a problem" + " as you creating on behalf user.");
                    // Ignore the error (bug11001)
                    PlatformUser findTemplate = new PlatformUser();
                    findTemplate.setUserId(password);
                    try {
                        customerUser = (PlatformUser) dm.getReferenceByBusinessKey(findTemplate);
                    } catch (ObjectNotFoundException e1) {
                        // impossible because we have got here after non unique
                        // business key exception has been
                        // thrown
                    }
                    break;
                }
                retryCount++;
                if (retryCount > 10) {
                    logger.logWarn(Log4jLogger.SYSTEM_LOG, e, LogMessageIdentifier.WARN_NON_UNIQUE_BUSINESS_KEY);
                    throw e;
                }
            } catch (MailOperationException e) {
                SaaSSystemException se = new SaaSSystemException(
                        "A mail operation failed although we didn't try to send a mail '"
                                + currentUser.getOrganization() + "'.",
                        e);
                logger.logError(Log4jLogger.SYSTEM_LOG, se, LogMessageIdentifier.ERROR_MAIL_OPERATION_FAILED);
                throw se;
            } catch (ValidationException e) {
                // this never happens
                SaaSSystemException se = new SaaSSystemException(
                        "The user creation failed with a validation exception.", e);
                logger.logError(Log4jLogger.SYSTEM_LOG, se,
                        LogMessageIdentifier.ERROR_USER_CREATION_FAILED_WITH_VALIDATION_ERROR);
                throw se;
            } catch (UserRoleAssignmentException e) {
                // this never happens
                SaaSSystemException se = new SaaSSystemException(
                        "The user creation failed with a user role assignment exception.", e);
                logger.logError(Log4jLogger.SYSTEM_LOG, se,
                        LogMessageIdentifier.ERROR_USER_CREATION_FAILED_WITH_VALIDATION_ERROR);
                throw se;
            }
        }
        return customerUser;
    }

    /**
     * Validates that the preconditions for the creation of a on-behalf user are
     * met.
     * 
     * @param organizationId
     *            The identifier of the customer organization the user should be
     *            created for.
     * @param password
     *            The password to be given to the u ser.
     * @param currentUser
     *            The caller and master user for the user to be created.
     * @return The customer organization.
     * @throws ObjectNotFoundException
     * @throws OperationNotPermittedException
     */
    private Organization validateForOnBehalfUserCreation(String organizationId, String password,
            PlatformUser currentUser) throws ObjectNotFoundException, OperationNotPermittedException {
        ArgumentValidator.notNull("organizationId", organizationId);
        ArgumentValidator.notNull("password", password);

        Organization customer = new Organization();
        customer.setOrganizationId(organizationId);
        customer = (Organization) dm.getReferenceByBusinessKey(customer);

        if (!currentUser.getOrganization().isActingOnBehalf(customer)) {
            OperationNotPermittedException onpe = new OperationNotPermittedException();
            logger.logWarn(Log4jLogger.SYSTEM_LOG, onpe, LogMessageIdentifier.WARN_USER_CREATE_CUSTOMER_FAILED,
                    currentUser.getUserId(), currentUser.getOrganization().getOrganizationId(),
                    customer.getOrganizationId());
            throw onpe;
        }
        return customer;
    }

    /**
     * Read the values from the remote LDAP and update the corresponding
     * platform user attributes.
     * 
     * @param pUser
     *            The platform user to be processed.
     * @throws ValidationException
     *             if not all mandatory LDAP parameters can be resolved for the
     *             underlying LDAP managed organization of the given user
     */
    void syncUserWithLdap(PlatformUser pUser) throws ValidationException {
        LdapConnector connector = getLdapConnectionForOrganization(pUser.getOrganization());
        Properties dirProperties = connector.getDirProperties();
        Map<SettingType, String> attrMap = connector.getAttrMap();
        String baseDN = connector.getBaseDN();

        List<SettingType> attrList = new ArrayList<>(attrMap.keySet());
        ILdapResultMapper<VOUserDetails> mapper = new LdapVOUserDetailsMapper(null, attrMap);
        try {
            List<VOUserDetails> list = ldapAccess.search(dirProperties, baseDN,
                    getLdapSearchFilter(attrMap, pUser.getUserId()), mapper, false);

            if (list.size() > 0) {
                UserDataAssembler.updatePlatformUser(list.get(0), attrList, pUser);
            }
        } catch (NamingException e) {
            SaaSSystemException se = new SaaSSystemException(
                    "The LDAP search for the user '" + pUser.getKey() + "' failed although the login succeeded.",
                    e);
            logger.logError(Log4jLogger.SYSTEM_LOG, se, LogMessageIdentifier.ERROR_LDAP_SEARCH_OF_USER_FAILED,
                    Long.toString(pUser.getKey()));
            throw se;
        }
    }

    private void setPassword(final PlatformUser user, final String password) {
        final long salt = random.nextLong();
        user.setPasswordSalt(salt);
        user.setPasswordHash(PasswordHash.calculateHash(salt, password));
    }

    /**
     * Returns the subscriptions UsageLicense corresponding with the given user.
     * 
     * @param user
     *            the user to find the usage license for
     * @param subscription
     *            the subscription to get the usage licenses from
     * @return The UsageLicense or <code>null</code> in case no matching license
     *         was found
     */
    private UsageLicense getUsgeLicenseForUserAndSubscription(PlatformUser user, Subscription subscription) {
        List<UsageLicense> licenses = subscription.getUsageLicenses();
        for (UsageLicense license : licenses) {
            if (license.getUser().getKey() == user.getKey()) {
                return license;
            }
        }
        return null;
    }

    /**
     * Tries to find the marketplace with the provided id. Returns
     * <code>null</code> if the id is <code>null</code> or empty.
     * 
     * @param marketplaceId
     *            the marketplace id
     * @return the {@link Marketplace} or <code>null</code>
     */
    private Marketplace getMarketplace(String marketplaceId) {
        if (Strings.isEmpty(marketplaceId)) {
            return null;
        }
        Marketplace marketplace = new Marketplace();
        marketplace.setMarketplaceId(marketplaceId);
        marketplace = (Marketplace) dm.find(marketplace);
        return marketplace;
    }

    private Organization getOrganization(String organizationId) throws ObjectNotFoundException {
        Organization template = new Organization();
        template.setOrganizationId(organizationId);
        return (Organization) dm.getReferenceByBusinessKey(template);
    }

    /**
     * The small helper function to create a user id which is unique in the
     * context of BES id the regular user id is not unique. The function follows
     * the scheme: 1. use email as user id 2. create string userid@orgid and us
     * it as user id.
     * 
     * @param userDetails
     *            Information about the user which should be created.
     * @return a globally unique user id.
     */
    private String createUniqueUserId(VOUserDetails userDetails) {
        String uniqueId = userDetails.getEMail();
        if (!isUserIdUnique(uniqueId)) {
            String orgId = userDetails.getOrganizationId();
            String userId = userDetails.getRealmUserId();
            uniqueId = userId + "@" + orgId;
            int i = 1;
            while (!isUserIdUnique(uniqueId) && i < 1000) {
                uniqueId = userDetails.getRealmUserId() + "_" + i;
                i++;
            }
        }
        return uniqueId;
    }

    /**
     * Checks if a user with the passed user id already exists.
     * 
     * @param userId
     *            the user id which should be checked.
     * @return <code>true</code> if the user id is unique, else false.
     */
    private boolean isUserIdUnique(String userId) {
        if (userId == null) {
            return false;
        }
        PlatformUser queryUser = new PlatformUser();
        queryUser.setUserId(userId);
        return (dm.find(queryUser) == null);
    }

    @Override
    @RolesAllowed("ORGANIZATION_ADMIN")
    public void setUserRoles(VOUser user, List<UserRoleType> roles)
            throws ObjectNotFoundException, UserModificationConstraintException, OperationNotPermittedException,
            UserRoleAssignmentException, UserActiveException {

        ArgumentValidator.notNull("user", user);
        ArgumentValidator.notNull("roles", roles);

        PlatformUser pUser = dm.getReference(PlatformUser.class, user.getKey());

        setUserRolesInt(new HashSet<>(roles), pUser);

    }

    @Override
    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public void setUserRolesInt(Set<UserRoleType> roles, PlatformUser pUser)
            throws UserModificationConstraintException, UserActiveException, OperationNotPermittedException,
            UserRoleAssignmentException, ObjectNotFoundException {

        List<UserRoleType> listForRevoke = new ArrayList<>();
        Iterator<RoleAssignment> roleIterator = pUser.getAssignedRoles().iterator();
        while (roleIterator.hasNext()) {
            RoleAssignment roleAssignment = roleIterator.next();
            if (!roles.contains(roleAssignment.getRole().getRoleName())
                    && !roleAssignment.getRole().getRoleName().isUnitRole()) {
                listForRevoke.add(roleAssignment.getRole().getRoleName());
            }
        }

        revokeUserRolesInt(pUser, listForRevoke);
        grantUserRoles(pUser, new ArrayList<>(roles));

    }

    @Override
    @RolesAllowed("ORGANIZATION_ADMIN")
    public List<UserRoleType> getAvailableUserRoles(VOUser user)
            throws ObjectNotFoundException, OperationNotPermittedException {

        ArgumentValidator.notNull("user", user);

        PlatformUser pUser = dm.getReference(PlatformUser.class, user.getKey());
        PermissionCheck.sameOrg(dm.getCurrentUser(), pUser, logger);

        Set<UserRoleType> roleSet = getAvailableUserRolesForUser(pUser);

        return new ArrayList<>(roleSet);
    }

    @Override
    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public Set<UserRoleType> getAvailableUserRolesForUser(PlatformUser pu) {

        Query query = dm.createNamedQuery("UserRole.getAllUserRoles");
        List<UserRole> userRoleList = ParameterizedTypes.list(query.getResultList(), UserRole.class);
        Organization org = pu.getOrganization();
        Set<UserRoleType> roleList = new HashSet<>();
        for (UserRole userRole : userRoleList) {
            if (isAllowedUserRole(org, userRole.getRoleName())) {
                roleList.add(userRole.getRoleName());
            }
        }

        return roleList;
    }

    @Override
    public boolean isUserLoggedIn(long userKey) {
        List<Session> sessionsForUserKey = sessionService.getSessionsForUserKey(userKey);
        return (sessionsForUserKey.size() > 0);
    }

    boolean isAllowedUserRole(Organization org, UserRoleType userRole) {
        if (isOrganizationSpecificRole(userRole)) {
            OrganizationRoleType orgTypesForUserRole = OrganizationRoleType
                    .correspondingOrgRoleForUserRole(userRole);
            if (!org.hasRole(orgTypesForUserRole)) {
                return false;
            }
        }
        return true;
    }

    /**
     * This method already exists in UserRoleType class, but there are problem
     * on Windows. The method cannot be found at runtime.
     */
    boolean isOrganizationSpecificRole(UserRoleType roleType) {
        return !(UserRoleType.ORGANIZATION_ADMIN.equals(roleType)
                || UserRoleType.SUBSCRIPTION_MANAGER.equals(roleType));
    }

    @Override
    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public boolean removeInactiveOnBehalfUsers() {
        return prepareForNewTransaction().removeInactiveOnBehalfUsersImpl();
    }

    private IdentityServiceLocal prepareForNewTransaction() {
        DateFactory.getInstance().takeCurrentTime();
        return sessionCtx.getBusinessObject(IdentityServiceLocal.class);
    }

    @Override
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public boolean removeInactiveOnBehalfUsersImpl() {
        ConfigurationSetting setting = cs.getConfigurationSetting(
                ConfigurationKey.PERMITTED_PERIOD_INACTIVE_ON_BEHALF_USERS, Configuration.GLOBAL_CONTEXT);
        long period = setting.getLongValue();
        Long lowerPeriodBound = Long.valueOf(System.currentTimeMillis() - period);
        List<OnBehalfUserReference> inactiveUsers = findInactiveOnBehalfUsers(lowerPeriodBound);
        for (OnBehalfUserReference toBeRemoved : inactiveUsers) {
            dm.remove(toBeRemoved);
        }
        return true;
    }

    @SuppressWarnings("unchecked")
    private List<OnBehalfUserReference> findInactiveOnBehalfUsers(Long lowerPeriodBound) {
        Query query = dm.createNamedQuery("OnBehalfUserReference.findInactiveBeforePeriod");
        query.setParameter("leastPermittedTime", lowerPeriodBound);
        return query.getResultList();
    }

    @Override
    public void refreshLdapUser() throws ValidationException {

        PlatformUser user = dm.getCurrentUser();
        if (user.getOrganization().isRemoteLdapActive()) {
            syncUserWithLdap(user);
        }

    }

    private void removeTrailingSlashes(StringBuffer url) {
        while (url.length() > 0 && url.charAt(url.length() - 1) == '/') {
            url.replace(url.length() - 1, url.length(), "");
        }
    }

    /**
     * Checks if the specified organization uses a remote LDAP system for user
     * authentication. If so, an UnsupportedOperationException will be thrown.
     * 
     * @param organization
     *            The organization to check.
     * @param messageId
     *            The log message identifier to use in case an exception is
     *            thrown.
     */
    private void ensureNoRemoteLdapUsed(Organization organization, LogMessageIdentifier messageId) {
        if (organization.isRemoteLdapActive()) {
            UnsupportedOperationException e = new UnsupportedOperationException(
                    "It is forbidden to perform this operation if a remote LDAP is active.");
            logger.logError(Log4jLogger.SYSTEM_LOG, e, messageId);

            sessionCtx.setRollbackOnly();
            throw e;
        }
    }

    @Override
    public boolean isCallerOrganizationAdmin() {
        return sessionCtx.isCallerInRole(UserRoleType.ORGANIZATION_ADMIN.name());
    }

    @Override
    @RolesAllowed("ORGANIZATION_ADMIN")
    public VOUserDetails createUserWithGroups(VOUserDetails user, List<UserRoleType> roles, String marketplaceId,
            Map<Long, UnitUserRole> userGroupKeyToRole) throws NonUniqueBusinessKeyException,
            MailOperationException, ValidationException, UserRoleAssignmentException, OperationPendingException {
        return createUser(user, roles, marketplaceId, userGroupKeyToRole);
    }

    private VOUserDetails createUser(VOUserDetails user, List<UserRoleType> roles, String marketplaceId,
            Map<Long, UnitUserRole> userGroupKeyToRole) throws NonUniqueBusinessKeyException,
            MailOperationException, ValidationException, UserRoleAssignmentException, OperationPendingException {
        ArgumentValidator.notNull("user", user);
        ArgumentValidator.notNull("roles", roles);

        // TODO DEL
        Tenant tenant = null;
        try {
            if (user.getTenantId() == null || user.getTenantId().equals("")) {
                Marketplace m = new Marketplace();
                m.setMarketplaceId(marketplaceId);
                Marketplace mp = (Marketplace) dm.getReferenceByBusinessKey(m);
                if (mp != null) {
                    tenant = mp.getTenant();
                }
            } else {
                Tenant t = new Tenant();
                t.setTenantId(user.getTenantId());
                tenant = (Tenant) dm.getReferenceByBusinessKey(t);
            }
        } catch (ObjectNotFoundException e) {
            e.printStackTrace();
        }

        checkIfUserExists(user.getUserId(), tenant);
        // TODO DEL

        TriggerProcessValidator validator = new TriggerProcessValidator(dm);
        if (validator.isRegisterOwnUserPending(user.getUserId())) {
            OperationPendingException ope = new OperationPendingException(String.format(
                    "Operation cannot be performed. There is already another pending request to register a user with conflicting id '%s'.",
                    user.getUserId()), OperationPendingException.ReasonEnum.REGISTER_USER_IN_OWN_ORGANIZATION,
                    new Object[] { user.getUserId() });
            logger.logWarn(Log4jLogger.SYSTEM_LOG, ope,
                    LogMessageIdentifier.WARN_CREATE_USER_FAILED_DUE_TO_TRIGGER_CONFLICT, user.getUserId());
            throw ope;
        }

        TriggerMessage message = new TriggerMessage(TriggerType.REGISTER_OWN_USER);
        List<TriggerProcessMessageData> list = triggerQS.sendSuspendingMessages(Collections.singletonList(message));
        TriggerProcess triggerProcess = list.get(0).getTrigger();
        triggerProcess.addTriggerProcessParameter(TriggerProcessParameterName.USER, user);
        triggerProcess.addTriggerProcessParameter(TriggerProcessParameterName.USER_ROLE_TYPE, roles);
        triggerProcess.addTriggerProcessParameter(TriggerProcessParameterName.MARKETPLACE_ID, marketplaceId);
        TriggerDefinition triggerDefinition = triggerProcess.getTriggerDefinition();
        triggerProcess.addTriggerProcessParameter(TriggerProcessParameterName.USER_GROUPS_WITH_ROLES,
                userGroupKeyToRole);

        VOUserDetails result = null;
        if (triggerDefinition == null) {
            try {
                result = createUserInt(triggerProcess);
            } catch (ValidationException | NonUniqueBusinessKeyException | MailOperationException
                    | UserRoleAssignmentException e) {
                sessionCtx.setRollbackOnly();
                throw e;
            }
        } else if (triggerDefinition.isSuspendProcess()) {
            triggerProcess.setTriggerProcessIdentifiers(
                    TriggerProcessIdentifiers.createRegisterOwnUser(dm, triggerDefinition.getType(), user));
            dm.merge(triggerProcess);
        }

        return result;
    }

    @Override
    @RolesAllowed("ORGANIZATION_ADMIN")
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public boolean addRevokeUserUnitAssignment(String unitName, List<VOUser> usersToBeAdded,
            List<VOUser> usersToBeRevoked) throws NonUniqueBusinessKeyException, ObjectNotFoundException,
            OperationNotPermittedException, MailOperationException {
        ArgumentValidator.notNull("unitName", unitName);
        ArgumentValidator.notNull("usersToBeAdded", usersToBeAdded);
        ArgumentValidator.notNull("usersToBeRevoked", usersToBeRevoked);

        List<PlatformUser> added = new ArrayList<>();
        List<PlatformUser> revoked = new ArrayList<>();
        Set<Long> onBehalfUserKeys = new HashSet<>();

        List<PlatformUser> platformUsers = dm.getCurrentUser().getOrganization().getPlatformUsers();
        UserGroup group = userGroupService.getUserGroupByName(unitName);
        for (PlatformUser user : platformUsers) {
            if (user.isOnBehalfUser()) {
                onBehalfUserKeys.add(Long.valueOf(user.getKey()));
            }
        }

        String currentUserTenant = dm.getCurrentUser().getTenantId();

        for (VOUser user : usersToBeAdded) {
            validateForOnBehalfUserGroupAssignment(user, onBehalfUserKeys);
            if (StringUtils.isBlank(user.getTenantId())) {
                user.setTenantId(currentUserTenant);
            }
            PlatformUser platformUser = new PlatformUser();
            platformUser.setUserId(user.getUserId());
            platformUser.setTenantId(user.getTenantId());
            added.add(platformUser);
        }
        for (VOUser user : usersToBeRevoked) {
            validateForOnBehalfUserGroupAssignment(user, onBehalfUserKeys);
            if (StringUtils.isBlank(user.getTenantId())) {
                user.setTenantId(currentUserTenant);
            }
            PlatformUser platformUser = new PlatformUser();
            platformUser.setUserId(user.getUserId());
            platformUser.setTenantId(user.getTenantId());
            revoked.add(platformUser);
        }

        if (!added.isEmpty() && group.isDefault()) {
            OperationNotPermittedException onpe = new OperationNotPermittedException();
            logger.logWarn(Log4jLogger.SYSTEM_LOG, onpe,
                    LogMessageIdentifier.WARN_DEFAULT_USERGROUP_OPERATION_NOT_PERMITTED);
            throw onpe;
        }

        try {
            userGroupService.assignUsersToGroup(group, added);
            userGroupService.revokeUsersFromGroup(group, revoked);
        } catch (NonUniqueBusinessKeyException | MailOperationException | OperationNotPermittedException e) {
            sessionCtx.setRollbackOnly();
            throw e;
        }
        return true;
    }

    private void validateForOnBehalfUserGroupAssignment(VOUser user, Set<Long> onBehalfUserKeys)
            throws OperationNotPermittedException {
        if (onBehalfUserKeys.contains(Long.valueOf(user.getKey()))) {
            OperationNotPermittedException onpe = new OperationNotPermittedException();
            logger.logWarn(Log4jLogger.SYSTEM_LOG, onpe,
                    LogMessageIdentifier.WARN_ADDREVOKE_USERGROUP_ASSIGNMENT_FOR_ONBEHALFUSER_NOT_PERMITTED,
                    user.getUserId());
            throw onpe;
        }
    }

    private void checkIfMarketplaceExists(String marketplaceId) throws ObjectNotFoundException {
        Marketplace marketplace = new Marketplace();
        marketplace.setMarketplaceId(marketplaceId);
        dm.getReferenceByBusinessKey(marketplace);
    }

    private boolean isAllSubscriptionExpired(List<Subscription> subscriptions) {
        boolean allExpired = true;
        for (Subscription subscription : subscriptions) {
            if (!subscription.getStatus().isExpired()) {
                allExpired = false;
                break;
            }
        }
        return allExpired;
    }

    @Override
    public PlatformUser getPlatformUserByOrganization(String userId, String orgId) throws ObjectNotFoundException {

        Query query = dm.createNamedQuery("PlatformUser.findByUserIdAndOrgId");

        query.setParameter("userId", userId);
        query.setParameter("organizationId", orgId);

        PlatformUser platformUser = null;

        try {
            platformUser = (PlatformUser) query.getSingleResult();
        } catch (NoResultException e) {
            throwONFExcp(userId);
        }

        if (platformUser == null) {
            throwONFExcp(userId);
        }

        return platformUser;
    }

    private void throwONFExcp(String userId) throws ObjectNotFoundException {

        ObjectNotFoundException onf = new ObjectNotFoundException(ObjectNotFoundException.ClassEnum.USER, userId);
        logger.logWarn(Log4jLogger.SYSTEM_LOG, onf, LogMessageIdentifier.WARN_USER_NOT_FOUND);
        throw onf;
    }
}