org.wso2.carbon.identity.provisioning.OutboundProvisioningManager.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.identity.provisioning.OutboundProvisioningManager.java

Source

/*
 * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.wso2.carbon.identity.provisioning;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.CarbonException;
import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.core.util.AnonymousSessionUtil;
import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException;
import org.wso2.carbon.identity.application.common.model.ClaimMapping;
import org.wso2.carbon.identity.application.common.model.IdentityProvider;
import org.wso2.carbon.identity.application.common.model.OutboundProvisioningConfig;
import org.wso2.carbon.identity.application.common.model.Property;
import org.wso2.carbon.identity.application.common.model.ProvisioningConnectorConfig;
import org.wso2.carbon.identity.application.common.model.RoleMapping;
import org.wso2.carbon.identity.application.common.model.ServiceProvider;
import org.wso2.carbon.identity.application.common.util.IdentityApplicationManagementUtil;
import org.wso2.carbon.identity.application.mgt.ApplicationManagementService;
import org.wso2.carbon.identity.provisioning.cache.ServiceProviderProvisioningConnectorCache;
import org.wso2.carbon.identity.provisioning.cache.ServiceProviderProvisioningConnectorCacheEntry;
import org.wso2.carbon.identity.provisioning.cache.ServiceProviderProvisioningConnectorCacheKey;
import org.wso2.carbon.identity.provisioning.dao.CacheBackedProvisioningMgtDAO;
import org.wso2.carbon.identity.provisioning.dao.ProvisioningManagementDAO;
import org.wso2.carbon.identity.provisioning.internal.IdentityProvisionServiceComponent;
import org.wso2.carbon.idp.mgt.IdentityProviderManagementException;
import org.wso2.carbon.idp.mgt.IdentityProviderManager;
import org.wso2.carbon.idp.mgt.util.IdPManagementUtil;
import org.wso2.carbon.registry.core.service.RegistryService;
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.core.UserCoreConstants;
import org.wso2.carbon.user.core.UserRealm;
import org.wso2.carbon.user.core.UserStoreManager;
import org.wso2.carbon.user.core.claim.Claim;
import org.wso2.carbon.user.core.service.RealmService;
import org.wso2.carbon.user.core.util.UserCoreUtil;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;

import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 *
 *
 */
public class OutboundProvisioningManager {

    private static final Log log = LogFactory.getLog(OutboundProvisioningManager.class);
    private static CacheBackedProvisioningMgtDAO dao = new CacheBackedProvisioningMgtDAO(
            new ProvisioningManagementDAO());

    private static OutboundProvisioningManager provisioningManager = new OutboundProvisioningManager();

    private OutboundProvisioningManager() {

    }

    /**
     * @return
     */
    public static OutboundProvisioningManager getInstance() {
        return provisioningManager;
    }

    /**
     * Get the tenant id of the given tenant domain.
     *
     * @param tenantDomain Tenant Domain
     * @return Tenant Id of domain user belongs to.
     * @throws IdentityApplicationManagementException Error when getting tenant id from tenant
     *                                                domain
     */
    private static int getTenantIdOfDomain(String tenantDomain) throws IdentityApplicationManagementException {

        try {
            return IdPManagementUtil.getTenantIdOfDomain(tenantDomain);
        } catch (UserStoreException e) {
            log.error(e.getMessage(), e);
            String msg = "Error occurred while getting Tenant Id from Tenant domain " + tenantDomain;
            throw new IdentityApplicationManagementException(msg);
        }
    }

    /**
     * TODO: Need to cache the output from this method.
     *
     * @return
     * @throws UserStoreException
     */
    private Map<String, RuntimeProvisioningConfig> getOutboundProvisioningConnectors(
            ServiceProvider serviceProvider, String tenantDomainName) throws IdentityProvisioningException {

        Map<String, RuntimeProvisioningConfig> connectors = new HashMap<>();

        // maintain the provisioning connector cache in the super tenant.
        // at the time of provisioning there may not be an authenticated user in the system -
        // specially in the case of in-bound provisioning.

        String tenantDomain = null;
        int tenantId = -1234;
        ServiceProviderProvisioningConnectorCacheKey key = null;
        ServiceProviderProvisioningConnectorCacheEntry entry = null;

        if (CarbonContext.getThreadLocalCarbonContext() != null) {
            tenantDomain = CarbonContext.getThreadLocalCarbonContext().getTenantDomain();
            tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
        }

        try {

            PrivilegedCarbonContext.startTenantFlow();
            PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext();
            carbonContext.setTenantId(MultitenantConstants.SUPER_TENANT_ID);
            carbonContext.setTenantDomain(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME);

            // reading from the cache
            key = new ServiceProviderProvisioningConnectorCacheKey(serviceProvider.getApplicationName(),
                    tenantDomain);

            entry = (ServiceProviderProvisioningConnectorCacheEntry) ServiceProviderProvisioningConnectorCache
                    .getInstance().getValueFromCache(key);

            // cache hit
            if (entry != null) {
                if (log.isDebugEnabled()) {
                    log.debug("Provisioning cache HIT for " + serviceProvider + " of " + tenantDomainName);
                }
                return entry.getConnectors();
            }

        } finally {
            PrivilegedCarbonContext.endTenantFlow();

            if (tenantDomain != null) {
                PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain);
                PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(tenantId);
            }
        }

        // NOW build the Map

        // a list of registered provisioning connector factories.
        Map<String, AbstractProvisioningConnectorFactory> registeredConnectorFactories = IdentityProvisionServiceComponent
                .getConnectorFactories();

        // get all registered list of out-bound provisioning connectors registered for the local
        // service provider.
        OutboundProvisioningConfig outboundProvisioningConfiguration = serviceProvider
                .getOutboundProvisioningConfig();

        if (outboundProvisioningConfiguration == null) {
            if (log.isDebugEnabled()) {
                log.debug("No outbound provisioning configuration defined for local service provider.");
            }
            // no out-bound provisioning configuration defined for local service provider.return an
            // empty list.
            return new HashMap<String, RuntimeProvisioningConfig>();
        }

        // get the list of registered provisioning identity providers in out-bound provisioning
        // configuration.
        IdentityProvider[] provisionningIdPList = outboundProvisioningConfiguration
                .getProvisioningIdentityProviders();

        if (provisionningIdPList != null && provisionningIdPList.length > 0) {
            // we have a set of provisioning identity providers registered in our system.

            for (IdentityProvider fIdP : provisionningIdPList) {
                // iterate through the provisioning identity provider list to find out the default
                // provisioning connector of each of the,

                try {

                    AbstractOutboundProvisioningConnector connector;

                    ProvisioningConnectorConfig defaultConnector = fIdP.getDefaultProvisioningConnectorConfig();
                    if (defaultConnector != null) {
                        // if no default provisioning connector defined for this identity provider,
                        // we can safely ignore it - need not to worry about provisioning.

                        String connectorType = fIdP.getDefaultProvisioningConnectorConfig().getName();

                        boolean enableJitProvisioning = false;

                        if (fIdP.getJustInTimeProvisioningConfig() != null
                                && fIdP.getJustInTimeProvisioningConfig().isProvisioningEnabled()) {
                            enableJitProvisioning = true;
                        }

                        connector = getOutboundProvisioningConnector(fIdP, registeredConnectorFactories,
                                tenantDomainName, enableJitProvisioning);
                        // add to the provisioning connectors list. there will be one item for each
                        // provisioning identity provider found in the out-bound provisioning
                        // configuration of the local service provider.
                        if (connector != null) {
                            RuntimeProvisioningConfig proConfig = new RuntimeProvisioningConfig();
                            proConfig.setProvisioningConnectorEntry(new SimpleEntry<>(connectorType, connector));
                            proConfig.setBlocking(defaultConnector.isBlocking());
                            connectors.put(fIdP.getIdentityProviderName(), proConfig);
                        }
                    }

                } catch (IdentityProviderManagementException e) {
                    throw new IdentityProvisioningException(
                            "Error while retrieving idp configuration for " + fIdP.getIdentityProviderName(), e);
                }
            }
        }

        try {

            PrivilegedCarbonContext.startTenantFlow();
            PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext();
            carbonContext.setTenantId(MultitenantConstants.SUPER_TENANT_ID);
            carbonContext.setTenantDomain(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME);

            entry = new ServiceProviderProvisioningConnectorCacheEntry();
            entry.setConnectors(connectors);
            ServiceProviderProvisioningConnectorCache.getInstance().addToCache(key, entry);

        } finally {
            PrivilegedCarbonContext.endTenantFlow();

            if (tenantDomain != null) {

                PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain);
                PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(tenantId);
            }
        }

        if (log.isDebugEnabled()) {
            log.debug("Entry added successfully ");
        }

        return connectors;
    }

    /**
     * @param fIdP
     * @param registeredConnectorFactories
     * @param tenantDomainName
     * @param enableJitProvisioning
     * @return
     * @throws IdentityProviderManagementException
     * @throws UserStoreException
     */
    private AbstractOutboundProvisioningConnector getOutboundProvisioningConnector(IdentityProvider fIdP,
            Map<String, AbstractProvisioningConnectorFactory> registeredConnectorFactories, String tenantDomainName,
            boolean enableJitProvisioning)
            throws IdentityProviderManagementException, IdentityProvisioningException {

        String idpName = fIdP.getIdentityProviderName();

        // name of the default provisioning connector.
        String connectorType = fIdP.getDefaultProvisioningConnectorConfig().getName();

        // get identity provider configuration.
        fIdP = IdentityProviderManager.getInstance().getEnabledIdPByName(idpName, tenantDomainName);

        if (fIdP == null) {
            // This is an exceptional situation. If service provider has connected to an
            // identity provider, that identity provider must be present in the system.
            // If not its an exception.
            throw new IdentityProvisioningException(
                    "Provisioning identity provider not available in the system. Idp Name : " + idpName);
        }

        // get a list of provisioning connectors associated with the provisioning
        // identity provider.
        ProvisioningConnectorConfig[] provisioningConfigs = fIdP.getProvisioningConnectorConfigs();

        if (provisioningConfigs != null && provisioningConfigs.length > 0) {

            for (ProvisioningConnectorConfig defaultProvisioningConfig : provisioningConfigs) {

                if (!connectorType.equals(defaultProvisioningConfig.getName())
                        || !defaultProvisioningConfig.isEnabled()) {
                    // we need to find the provisioning connector selected by the service provider.
                    continue;
                }

                // this is how we match the configuration to the runtime. the provisioning
                // connector factory should be registered with the system, with the exact
                // name available in the corresponding configuration.
                AbstractProvisioningConnectorFactory factory = registeredConnectorFactories.get(connectorType);

                // get the provisioning properties associated with a given provisioning
                // connector.
                Property[] provisioningProperties = defaultProvisioningConfig.getProvisioningProperties();

                if (enableJitProvisioning) {
                    Property jitEnabled = new Property();
                    jitEnabled.setName(IdentityProvisioningConstants.JIT_PROVISIONING_ENABLED);
                    jitEnabled.setValue("1");
                    provisioningProperties = IdentityApplicationManagementUtil.concatArrays(provisioningProperties,
                            new Property[] { jitEnabled });
                }

                Property userIdClaimURL = new Property();
                userIdClaimURL.setName("userIdClaimUri");

                if (fIdP.getClaimConfig() != null && fIdP.getClaimConfig().getUserClaimURI() != null) {
                    userIdClaimURL.setValue(fIdP.getClaimConfig().getUserClaimURI());
                } else {
                    userIdClaimURL.setValue("");
                }

                List<Property> provisioningPropertiesList = new ArrayList<>(Arrays.asList(provisioningProperties));

                provisioningPropertiesList.add(userIdClaimURL);

                provisioningProperties = new Property[provisioningPropertiesList.size()];
                provisioningProperties = provisioningPropertiesList.toArray(provisioningProperties);

                // get the runtime provisioning connector associate the provisioning
                // identity provider. any given time, a given provisioning identity provider
                // can only be associated with a single provisioning connector.
                return factory.getConnector(idpName, provisioningProperties, tenantDomainName);
            }
        }

        return null;
    }

    /**
     * @param provisioningEntity
     * @param serviceProviderIdentifier
     * @param inboundClaimDialect
     * @param tenantDomainName
     * @param jitProvisioning
     * @throws IdentityProvisioningException
     */
    public void provision(ProvisioningEntity provisioningEntity, String serviceProviderIdentifier,
            String inboundClaimDialect, String tenantDomainName, boolean jitProvisioning)
            throws IdentityProvisioningException {

        try {

            // get details about the service provider.any in-bound provisioning request via
            // the SOAP based API (or the management console) - or SCIM API with HTTP Basic
            // Authentication is considered as coming from the local service provider.
            ServiceProvider serviceProvider = ApplicationManagementService.getInstance()
                    .getServiceProvider(serviceProviderIdentifier, tenantDomainName);

            if (serviceProvider == null) {
                throw new IdentityProvisioningException(
                        "Invalid service provider name : " + serviceProviderIdentifier);
            }

            ClaimMapping[] spClaimMappings = null;

            // if we know the serviceProviderClaimDialect - we do not need to find it again.
            if (inboundClaimDialect == null && serviceProvider.getClaimConfig() != null) {
                spClaimMappings = serviceProvider.getClaimConfig().getClaimMappings();
            }

            // get all the provisioning connectors associated with local service provider for
            // out-bound provisioning.
            // TODO: stop loading connectors all the time.
            Map<String, RuntimeProvisioningConfig> connectors = getOutboundProvisioningConnectors(serviceProvider,
                    tenantDomainName);

            ProvisioningEntity outboundProEntity;

            ExecutorService executors = null;

            if (MapUtils.isNotEmpty(connectors)) {
                executors = Executors.newFixedThreadPool(connectors.size());
            }

            for (Iterator<Entry<String, RuntimeProvisioningConfig>> iterator = connectors.entrySet()
                    .iterator(); iterator.hasNext();) {

                Entry<String, RuntimeProvisioningConfig> entry = iterator.next();

                Entry<String, AbstractOutboundProvisioningConnector> connectorEntry = entry.getValue()
                        .getProvisioningConnectorEntry();

                AbstractOutboundProvisioningConnector connector = connectorEntry.getValue();
                String connectorType = connectorEntry.getKey();
                String idPName = entry.getKey();

                IdentityProvider provisioningIdp = IdentityProviderManager.getInstance().getIdPByName(idPName,
                        tenantDomainName);

                if (provisioningIdp == null) {
                    // this is an exception if we cannot find the provisioning identity provider
                    // by its name.
                    throw new IdentityProvisioningException("Invalid identity provider name : " + idPName);
                }

                String outboundClaimDialect = connector.getClaimDialectUri();

                if (outboundClaimDialect == null && (provisioningIdp.getClaimConfig() == null
                        || provisioningIdp.getClaimConfig().isLocalClaimDialect())) {
                    outboundClaimDialect = IdentityProvisioningConstants.WSO2_CARBON_DIALECT;
                }

                ClaimMapping[] idpClaimMappings = null;

                if (provisioningIdp.getClaimConfig() != null) {
                    idpClaimMappings = provisioningIdp.getClaimConfig().getClaimMappings();
                }

                // TODO: this should happen asynchronously in a different thread.
                // create a new provisioning entity object for each provisioning identity
                // provider.

                Map<ClaimMapping, List<String>> mapppedClaims;

                // get mapped claims.
                mapppedClaims = getMappedClaims(inboundClaimDialect, outboundClaimDialect, provisioningEntity,
                        spClaimMappings, idpClaimMappings, tenantDomainName);

                if (provisioningIdp.getPermissionAndRoleConfig() != null) {
                    // update with mapped user groups.
                    updateProvisioningUserWithMappedRoles(provisioningEntity,
                            provisioningIdp.getPermissionAndRoleConfig().getRoleMappings());
                }

                // check whether we already have the provisioned identifier - if
                // so set it.
                ProvisionedIdentifier provisionedIdentifier;

                provisionedIdentifier = getProvisionedEntityIdentifier(idPName, connectorType, provisioningEntity,
                        tenantDomainName);

                ProvisioningOperation provisioningOp = provisioningEntity.getOperation();

                if (provisionedIdentifier == null || provisionedIdentifier.getIdentifier() == null) {
                    provisioningOp = ProvisioningOperation.POST;
                }

                String[] provisionByRoleList = new String[0];

                if (provisioningIdp.getProvisioningRole() != null) {
                    provisionByRoleList = provisioningIdp.getProvisioningRole().split(",");
                }

                if (provisioningEntity.getEntityType() == ProvisioningEntityType.GROUP
                        && Arrays.asList(provisionByRoleList).contains(provisioningEntity.getEntityName())) {
                    Map<ClaimMapping, List<String>> attributes = provisioningEntity.getAttributes();
                    List<String> newUsersList = attributes.get(ClaimMapping
                            .build(IdentityProvisioningConstants.NEW_USER_CLAIM_URI, null, null, false));

                    List<String> deletedUsersList = attributes.get(ClaimMapping
                            .build(IdentityProvisioningConstants.DELETED_USER_CLAIM_URI, null, null, false));

                    Map<ClaimMapping, List<String>> mappedUserClaims;
                    ProvisionedIdentifier provisionedUserIdentifier;

                    for (String user : newUsersList) {
                        ProvisioningEntity inboundProvisioningEntity = getInboundProvisioningEntity(
                                provisioningEntity, tenantDomainName, ProvisioningOperation.POST, user);

                        provisionedUserIdentifier = getProvisionedEntityIdentifier(idPName, connectorType,
                                inboundProvisioningEntity, tenantDomainName);

                        if (provisionedUserIdentifier != null
                                && provisionedUserIdentifier.getIdentifier() != null) {
                            continue;
                        }

                        mappedUserClaims = getMappedClaims(inboundClaimDialect, outboundClaimDialect,
                                inboundProvisioningEntity, spClaimMappings, idpClaimMappings, tenantDomainName);

                        outboundProEntity = new ProvisioningEntity(ProvisioningEntityType.USER, user,
                                ProvisioningOperation.POST, mappedUserClaims);
                        Callable<Boolean> proThread = new ProvisioningThread(outboundProEntity, tenantDomainName,
                                connector, connectorType, idPName, dao);
                        outboundProEntity.setIdentifier(provisionedIdentifier);
                        outboundProEntity.setJitProvisioning(jitProvisioning);
                        boolean isBlocking = entry.getValue().isBlocking();
                        executeOutboundProvisioning(provisioningEntity, executors, connectorType, idPName,
                                proThread, isBlocking);

                    }

                    for (String user : deletedUsersList) {

                        ProvisioningEntity inboundProvisioningEntity = getInboundProvisioningEntity(
                                provisioningEntity, tenantDomainName, ProvisioningOperation.DELETE, user);

                        provisionedUserIdentifier = getProvisionedEntityIdentifier(idPName, connectorType,
                                inboundProvisioningEntity, tenantDomainName);

                        if (provisionedUserIdentifier != null
                                && provisionedUserIdentifier.getIdentifier() != null) {
                            mappedUserClaims = getMappedClaims(inboundClaimDialect, outboundClaimDialect,
                                    inboundProvisioningEntity, spClaimMappings, idpClaimMappings, tenantDomainName);

                            outboundProEntity = new ProvisioningEntity(ProvisioningEntityType.USER, user,
                                    ProvisioningOperation.DELETE, mappedUserClaims);
                            Callable<Boolean> proThread = new ProvisioningThread(outboundProEntity,
                                    tenantDomainName, connector, connectorType, idPName, dao);
                            outboundProEntity.setIdentifier(provisionedUserIdentifier);
                            outboundProEntity.setJitProvisioning(jitProvisioning);
                            boolean isBlocking = entry.getValue().isBlocking();
                            executeOutboundProvisioning(provisioningEntity, executors, connectorType, idPName,
                                    proThread, isBlocking);
                        }
                    }

                } else {
                    // see whether the given provisioning entity satisfies the conditions to be
                    // provisioned.

                    if (!canUserBeProvisioned(provisioningEntity, provisionByRoleList, tenantDomainName)) {
                        if (!canUserBeDeProvisioned(provisionedIdentifier)) {
                            continue;
                        } else {
                            // This is used when user removed from the provisioning role
                            provisioningOp = ProvisioningOperation.DELETE;
                        }
                    }

                    outboundProEntity = new ProvisioningEntity(provisioningEntity.getEntityType(),
                            provisioningEntity.getEntityName(), provisioningOp, mapppedClaims);

                    Callable<Boolean> proThread = new ProvisioningThread(outboundProEntity, tenantDomainName,
                            connector, connectorType, idPName, dao);
                    outboundProEntity.setIdentifier(provisionedIdentifier);
                    outboundProEntity.setJitProvisioning(jitProvisioning);
                    boolean isBlocking = entry.getValue().isBlocking();
                    executeOutboundProvisioning(provisioningEntity, executors, connectorType, idPName, proThread,
                            isBlocking);
                }

            }

            if (executors != null) {
                executors.shutdown();
            }

        } catch (CarbonException | IdentityApplicationManagementException | IdentityProviderManagementException
                | UserStoreException e) {
            throw new IdentityProvisioningException("Error occurred while checking for user " + "provisioning", e);
        }
    }

    private void executeOutboundProvisioning(ProvisioningEntity provisioningEntity, ExecutorService executors,
            String connectorType, String idPName, Callable<Boolean> proThread, boolean isBlocking)
            throws IdentityProvisioningException {
        if (!isBlocking) {
            executors.submit(proThread);
        } else {
            try {

                boolean success = proThread.call();
                if (!success) {
                    if (executors != null) {
                        executors.shutdown();
                    }
                    throw new IdentityProvisioningException(generateMessageOnFailureProvisioningOperation(idPName,
                            connectorType, provisioningEntity));
                    //DO Rollback
                }
            } catch (Exception e) { //call() of Callable interface throws this exception
                handleException(idPName, connectorType, provisioningEntity, executors, e);
            }
        }
    }

    private ProvisioningEntity getInboundProvisioningEntity(ProvisioningEntity provisioningEntity,
            String tenantDomain, ProvisioningOperation operation, String userName)
            throws CarbonException, UserStoreException {
        Map<ClaimMapping, List<String>> outboundAttributes = new HashMap<>();

        if (userName != null) {
            outboundAttributes.put(
                    ClaimMapping.build(IdentityProvisioningConstants.USERNAME_CLAIM_URI, null, null, false),
                    Arrays.asList(new String[] { userName }));
        }
        List<String> roleListOfUser = getUserRoles(userName, tenantDomain);
        if (roleListOfUser != null) {
            outboundAttributes.put(
                    ClaimMapping.build(IdentityProvisioningConstants.GROUP_CLAIM_URI, null, null, false),
                    roleListOfUser);
        }

        String domainAwareName = userName;

        String domainName = getDomainFromName(provisioningEntity.getEntityName());
        if (domainName != null && !domainName.equals(UserCoreConstants.INTERNAL_DOMAIN)) {
            if (log.isDebugEnabled()) {
                log.debug("Adding domain name : " + domainName + " to user : " + userName);
            }
            domainAwareName = UserCoreUtil.addDomainToName(userName, domainName);
        }
        ProvisioningEntity inboundProvisioningEntity = new ProvisioningEntity(ProvisioningEntityType.USER,
                domainAwareName, operation, outboundAttributes);
        inboundProvisioningEntity.setInboundAttributes(getUserClaims(userName, tenantDomain));
        return inboundProvisioningEntity;
    }

    private String generateMessageOnFailureProvisioningOperation(String idPName, String connectorType,
            ProvisioningEntity provisioningEntity) {
        if (log.isDebugEnabled()) {
            String errMsg = "Provisioning failed for IDP = " + idPName + " " + "Connector Type =" + connectorType
                    + " ";

            errMsg += " Provisioned entity name = " + provisioningEntity.getEntityName() + " For operation = "
                    + provisioningEntity.getOperation() + " " + "failed  ";

            log.error(errMsg);
        }
        return "Provisioning failed for IDP = " + idPName + " " + "with Entity name="
                + provisioningEntity.getEntityName();
    }

    /**
     * @param provisioningEntity
     * @param idPRoleMapping
     */
    private void updateProvisioningUserWithMappedRoles(ProvisioningEntity provisioningEntity,
            RoleMapping[] idPRoleMapping) {

        if (provisioningEntity.getEntityType() != ProvisioningEntityType.USER || idPRoleMapping == null
                || idPRoleMapping.length == 0) {
            return;
        }

        List<String> userGroups = getGroupNames(provisioningEntity.getAttributes());

        if (CollectionUtils.isEmpty(userGroups)) {
            return;
        }

        Map<String, String> mappedRoles = new HashMap<>();

        for (RoleMapping mapping : idPRoleMapping) {
            mappedRoles.put(mapping.getLocalRole().getLocalRoleName(), mapping.getRemoteRole());
        }

        List<String> mappedUserGroups = new ArrayList<>();

        for (Iterator<String> iterator = userGroups.iterator(); iterator.hasNext();) {
            String userGroup = iterator.next();
            String mappedGroup = null;
            if ((mappedGroup = mappedRoles.get(userGroup)) != null) {
                mappedUserGroups.add(mappedGroup);
            }
        }

        ProvisioningUtil.setClaimValue(IdentityProvisioningConstants.GROUP_CLAIM_URI,
                provisioningEntity.getAttributes(), mappedUserGroups);

    }

    /**
     * @param inboundClaimDialect
     * @param outboundClaimDialect
     * @param provisioningEntity
     * @param spClaimMappings
     * @param idpClaimMappings
     * @return
     * @throws IdentityApplicationManagementException
     */
    private Map<ClaimMapping, List<String>> getMappedClaims(String inboundClaimDialect, String outboundClaimDialect,
            ProvisioningEntity provisioningEntity, ClaimMapping[] spClaimMappings, ClaimMapping[] idpClaimMappings,
            String tenantDomainName) throws IdentityApplicationManagementException {

        // if we have any in-bound attributes - need to convert those into out-bound
        // attributes in a form understood by the external provisioning providers.
        Map<String, String> inboundAttributes = provisioningEntity.getInboundAttributes();

        if (outboundClaimDialect != null) {
            // out-bound claim dialect is not provisioning provider specific. Its
            // specific to the connector.

            if (inboundClaimDialect == null) {
                // in-bound claim dialect is service provider specific.
                // we have read the claim mapping from service provider claim
                // configuration.
                return IdentityApplicationManagementUtil.getMappedClaims(outboundClaimDialect, inboundAttributes,
                        spClaimMappings, provisioningEntity.getAttributes(), tenantDomainName);
            } else {
                // in-bound claim dialect is not service provider specific.
                // its been supplied by the corresponding in-bound provisioning servlet
                // or listener.
                return IdentityApplicationManagementUtil.getMappedClaims(outboundClaimDialect, inboundAttributes,
                        inboundClaimDialect, provisioningEntity.getAttributes(), tenantDomainName);
            }
        } else {
            // out-bound claim dialect is provisioning provider specific.
            // we have read the claim mapping from identity provider claim
            // configuration

            if (inboundClaimDialect == null) {
                // in-bound claim dialect is service provider specific.
                // we have read the claim mapping from service provider claim
                // configuration.
                return IdentityApplicationManagementUtil.getMappedClaims(idpClaimMappings, inboundAttributes,
                        spClaimMappings, provisioningEntity.getAttributes());
            } else {
                // in-bound claim dialect is not service provider specific.
                // its been supplied by the corresponding in-bound provisioning servlet
                // or listener.
                return IdentityApplicationManagementUtil.getMappedClaims(idpClaimMappings, inboundAttributes,
                        inboundClaimDialect, provisioningEntity.getAttributes(), tenantDomainName);
            }
        }
    }

    /**
     * @param attributeMap
     * @return
     */
    protected List<String> getGroupNames(Map<ClaimMapping, List<String>> attributeMap) {
        return ProvisioningUtil.getClaimValues(attributeMap, IdentityProvisioningConstants.GROUP_CLAIM_URI, null);
    }

    /**
     * @param attributeMap
     * @return
     */
    private String getUserName(Map<ClaimMapping, List<String>> attributeMap) {
        List<String> userList = ProvisioningUtil.getClaimValues(attributeMap,
                IdentityProvisioningConstants.USERNAME_CLAIM_URI, null);

        if (CollectionUtils.isNotEmpty(userList)) {
            return userList.get(0);
        }

        return null;
    }

    /**
     * @param provisioningEntity
     * @param provisionByRoleList
     * @param tenantDomain
     * @return
     * @throws CarbonException
     * @throws UserStoreException
     */
    protected boolean canUserBeProvisioned(ProvisioningEntity provisioningEntity, String[] provisionByRoleList,
            String tenantDomain) throws UserStoreException, CarbonException {

        if (provisioningEntity.getEntityType() != ProvisioningEntityType.USER || provisionByRoleList == null
                || provisionByRoleList.length == 0) {
            // we apply restrictions only for users.
            // if service provider's out-bound provisioning configuration does not define any roles
            // to be provisioned then we apply no restrictions.
            return true;
        }

        String userName = getUserName(provisioningEntity.getAttributes());
        List<String> roleListOfUser = getUserRoles(userName, tenantDomain);

        for (String provisionByRole : provisionByRoleList) {
            if (roleListOfUser.contains(provisionByRole)) {
                return true;
            }
        }

        return false;
    }

    /**
     * @param provisionedIdentifier
     * @return
     * @throws CarbonException
     * @throws UserStoreException
     */
    protected boolean canUserBeDeProvisioned(ProvisionedIdentifier provisionedIdentifier)
            throws UserStoreException, CarbonException, IdentityApplicationManagementException {

        // check whether we already have the provisioned identifier.current idp is not eligible to
        // provisioning.
        if (provisionedIdentifier != null && provisionedIdentifier.getIdentifier() != null) {
            return true;
        }

        return false;
    }

    /**
     * @param userName
     * @param tenantDomain
     * @return
     * @throws CarbonException
     * @throws UserStoreException
     */
    private List<String> getUserRoles(String userName, String tenantDomain)
            throws CarbonException, UserStoreException {

        RegistryService registryService = IdentityProvisionServiceComponent.getRegistryService();
        RealmService realmService = IdentityProvisionServiceComponent.getRealmService();

        UserRealm realm = AnonymousSessionUtil.getRealmByTenantDomain(registryService, realmService, tenantDomain);

        UserStoreManager userstore = null;
        userstore = realm.getUserStoreManager();
        String[] newRoles = userstore.getRoleListOfUser(userName);
        return Arrays.asList(newRoles);
    }

    /**
     * @param userName
     * @param tenantDomain
     * @return
     * @throws CarbonException
     * @throws UserStoreException
     */
    private Map<String, String> getUserClaims(String userName, String tenantDomain)
            throws CarbonException, UserStoreException {

        Map<String, String> inboundAttributes = new HashMap<>();

        RegistryService registryService = IdentityProvisionServiceComponent.getRegistryService();
        RealmService realmService = IdentityProvisionServiceComponent.getRealmService();

        UserRealm realm = AnonymousSessionUtil.getRealmByTenantDomain(registryService, realmService, tenantDomain);

        UserStoreManager userstore = null;
        userstore = realm.getUserStoreManager();
        Claim[] claimArray = null;
        try {
            claimArray = userstore.getUserClaimValues(userName, null);
        } catch (UserStoreException e) {
            if (e.getMessage().contains("UserNotFound")) {
                if (log.isDebugEnabled()) {
                    log.debug("User " + userName + " not found in user store");
                }
            } else {
                throw e;
            }
        }
        if (claimArray != null) {
            for (Claim claim : claimArray) {
                inboundAttributes.put(claim.getClaimUri(), claim.getValue());
            }
        }

        return inboundAttributes;
    }

    private String getUserIdClaimValue(String userIdClaimURI, String tenantDomainName) {
        return null;
    }

    /**
     * @param idpName
     * @param connectorType
     * @param provisioningEntity
     * @param tenantDomain
     * @return
     * @throws IdentityApplicationManagementException
     */
    private ProvisionedIdentifier getProvisionedEntityIdentifier(String idpName, String connectorType,
            ProvisioningEntity provisioningEntity, String tenantDomain)
            throws IdentityApplicationManagementException {
        int tenantId = getTenantIdOfDomain(tenantDomain);
        return dao.getProvisionedIdentifier(idpName, connectorType, provisioningEntity, tenantId, tenantDomain);
    }

    private String getDomainFromName(String name) {
        int index;
        if ((index = name.indexOf("/")) > 0) {
            String domain = name.substring(0, index);
            return domain;
        }
        return UserCoreConstants.PRIMARY_DEFAULT_DOMAIN_NAME;
    }

    /**
     * introduce extendability for handling provisioning exceptions
     *
     * @param idPName
     * @param connectorType
     * @param provisioningEntity
     * @param executors
     * @param e
     */
    protected void handleException(String idPName, String connectorType, ProvisioningEntity provisioningEntity,
            ExecutorService executors, Exception e) {

        if (log.isDebugEnabled()) {
            log.debug(generateMessageOnFailureProvisioningOperation(idPName, connectorType, provisioningEntity), e);
        }
    }
}