diff --git a/components/provisioning/org.wso2.carbon.identity.provisioning/pom.xml b/components/provisioning/org.wso2.carbon.identity.provisioning/pom.xml index 6799fd0af89c..7cf62cd716f5 100644 --- a/components/provisioning/org.wso2.carbon.identity.provisioning/pom.xml +++ b/components/provisioning/org.wso2.carbon.identity.provisioning/pom.xml @@ -92,6 +92,10 @@ org.wso2.carbon.identity.framework org.wso2.carbon.identity.application.authentication.framework + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.central.log.mgt + org.testng testng @@ -173,6 +177,8 @@ version="${carbon.identity.package.import.version.range}", org.wso2.carbon.identity.application.authentication.framework.util; version="${carbon.identity.package.import.version.range}", + org.wso2.carbon.identity.central.log.mgt.utils; + version="${carbon.identity.package.import.version.range}", !org.wso2.carbon.identity.provisioning.internal, diff --git a/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/OutboundProvisioningManager.java b/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/OutboundProvisioningManager.java index d274d3ad5719..72dc8514ad32 100644 --- a/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/OutboundProvisioningManager.java +++ b/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/OutboundProvisioningManager.java @@ -204,24 +204,17 @@ private Map getOutboundProvisioningConnectors String connectorType = fIdP.getDefaultProvisioningConnectorConfig() .getName(); - boolean enableJitProvisioning = false; + boolean enableJitProvisioning = fIdP.getJustInTimeProvisioningConfig() != null + && fIdP.getJustInTimeProvisioningConfig().isProvisioningEnabled(); - if (fIdP.getJustInTimeProvisioningConfig() != null - && fIdP.getJustInTimeProvisioningConfig().isProvisioningEnabled()) { - enableJitProvisioning = true; - } - - connector = getOutboundProvisioningConnector(fIdP, - registeredConnectorFactories, tenantDomain, - enableJitProvisioning); + connector = getOutboundProvisioningConnector(fIdP, registeredConnectorFactories, tenantDomain); // 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.setJitProvisioningEnabled(enableJitProvisioning); + proConfig.setProvisioningConnectorEntry(new SimpleEntry<>(connectorType, connector)); proConfig.setBlocking(defaultConnector.isBlocking()); proConfig.setPolicyEnabled(defaultConnector.isRulesEnabled()); connectors.put(fIdP.getIdentityProviderName(), proConfig); @@ -250,7 +243,6 @@ private Map getOutboundProvisioningConnectors * @param fIdP * @param registeredConnectorFactories * @param tenantDomainName - * @param enableJitProvisioning * @return * @throws IdentityProviderManagementException * @throws UserStoreException @@ -258,7 +250,7 @@ private Map getOutboundProvisioningConnectors private AbstractOutboundProvisioningConnector getOutboundProvisioningConnector( IdentityProvider fIdP, Map registeredConnectorFactories, - String tenantDomainName, boolean enableJitProvisioning) + String tenantDomainName) throws IdentityProviderManagementException, IdentityProvisioningException { String idpName = fIdP.getIdentityProviderName(); @@ -303,13 +295,17 @@ private AbstractOutboundProvisioningConnector getOutboundProvisioningConnector( 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}); - } + /* + * Always set JIT provisioning as enabled at the connector level. The actual JIT enablement check is + * performed in ProvisioningThread before executing outbound provisioning for JIT provisioned entity. + * Therefore, validating JIT conditions at the connector level is unnecessary and may cause issues + * when the same connector is used by applications with different JIT configurations. + */ + 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"); @@ -357,9 +353,7 @@ public void provision(ProvisioningEntity provisioningEntity, String serviceProvi } // Any provisioning request coming via Console, considered as coming from the resident SP. - // If the application based outbound provisioning is disabled, resident SP configuration will be used. - if (StringUtils.equals(CONSOLE_APPLICATION_NAME, serviceProviderIdentifier) || - !isApplicationBasedOutboundProvisioningEnabled()) { + if (StringUtils.equals(CONSOLE_APPLICATION_NAME, serviceProviderIdentifier)) { serviceProviderIdentifier = LOCAL_SP; inboundClaimDialect = IdentityProvisioningConstants.WSO2_CARBON_DIALECT; } @@ -375,11 +369,6 @@ public void provision(ProvisioningEntity provisioningEntity, String serviceProvi + serviceProviderIdentifier); } - String provisioningEntityTenantDomainName = spTenantDomainName; - if (serviceProvider.isSaasApp() && isUserTenantBasedOutboundProvisioningEnabled()) { - provisioningEntityTenantDomainName = CarbonContext.getThreadLocalCarbonContext().getTenantDomain(); - } - ClaimMapping[] spClaimMappings = null; // if we know the serviceProviderClaimDialect - we do not need to find it again. @@ -393,6 +382,32 @@ public void provision(ProvisioningEntity provisioningEntity, String serviceProvi Map connectors = getOutboundProvisioningConnectors(serviceProvider, spTenantDomainName); + // When application-based outbound provisioning is disabled and the application has no + // outbound provisioning connectors configured, fall back to LOCAL_SP (resident app) connectors. + if (!isApplicationBasedOutboundProvisioningEnabled() && MapUtils.isEmpty(connectors) + && !LOCAL_SP.equals(serviceProviderIdentifier)) { + ServiceProvider localSP = ApplicationManagementService.getInstance() + .getServiceProvider(LOCAL_SP, spTenantDomainName); + if (localSP != null) { + if (log.isDebugEnabled()) { + log.debug(String.format( + "No outbound provisioning connectors are configured for the application: %s. " + + "Falling back to resident application outbound provisioning connectors.", + serviceProviderIdentifier)); + } + serviceProvider = localSP; + connectors = getOutboundProvisioningConnectors(localSP, spTenantDomainName); + inboundClaimDialect = IdentityProvisioningConstants.WSO2_CARBON_DIALECT; + // LOCAL_SP uses WSO2_CARBON_DIALECT; spClaimMappings is not needed. + spClaimMappings = null; + } + } + + String provisioningEntityTenantDomainName = spTenantDomainName; + if (serviceProvider.isSaasApp() && isUserTenantBasedOutboundProvisioningEnabled()) { + provisioningEntityTenantDomainName = CarbonContext.getThreadLocalCarbonContext().getTenantDomain(); + } + ProvisioningEntity outboundProEntity; ExecutorService executors = null; @@ -412,6 +427,7 @@ public void provision(ProvisioningEntity provisioningEntity, String serviceProvi AbstractOutboundProvisioningConnector connector = connectorEntry.getValue(); String connectorType = connectorEntry.getKey(); String idPName = entry.getKey(); + boolean jitProvisioningEnabledForIdP = entry.getValue().isJitProvisioningEnabled(); IdentityProvider provisioningIdp = IdentityProviderManager.getInstance().getIdPByName(idPName, spTenantDomainName); @@ -535,7 +551,8 @@ public void provision(ProvisioningEntity provisioningEntity, String serviceProvi outboundProEntity = new ProvisioningEntity(ProvisioningEntityType.USER, user, ProvisioningOperation.POST, mappedUserClaims); Callable proThread = new ProvisioningThread(outboundProEntity, spTenantDomainName, - provisioningEntityTenantDomainName, connector, connectorType, idPName, dao); + provisioningEntityTenantDomainName, connector, connectorType, idPName, dao, + jitProvisioningEnabledForIdP); outboundProEntity.setIdentifier(provisionedIdentifier); outboundProEntity.setJitProvisioning(jitProvisioning); boolean isBlocking = entry.getValue().isBlocking(); @@ -560,7 +577,8 @@ public void provision(ProvisioningEntity provisioningEntity, String serviceProvi outboundProEntity = new ProvisioningEntity(ProvisioningEntityType.USER, user, ProvisioningOperation.DELETE, mappedUserClaims); Callable proThread = new ProvisioningThread(outboundProEntity, spTenantDomainName, - provisioningEntityTenantDomainName, connector, connectorType, idPName, dao); + provisioningEntityTenantDomainName, connector, connectorType, idPName, dao, + jitProvisioningEnabledForIdP); outboundProEntity.setIdentifier(provisionedUserIdentifier); outboundProEntity.setJitProvisioning(jitProvisioning); boolean isBlocking = entry.getValue().isBlocking(); @@ -586,7 +604,8 @@ public void provision(ProvisioningEntity provisioningEntity, String serviceProvi provisioningEntity.getEntityName(), provisioningOp, mapppedClaims); Callable proThread = new ProvisioningThread(outboundProEntity, spTenantDomainName, - provisioningEntityTenantDomainName, connector, connectorType, idPName, dao); + provisioningEntityTenantDomainName, connector, connectorType, idPName, dao, + jitProvisioningEnabledForIdP); outboundProEntity.setIdentifier(provisionedIdentifier); outboundProEntity.setJitProvisioning(jitProvisioning); boolean isAllowed = true; diff --git a/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/ProvisioningThread.java b/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/ProvisioningThread.java index c251a2dfdcd2..4c8c3c0d69ed 100644 --- a/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/ProvisioningThread.java +++ b/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/ProvisioningThread.java @@ -18,7 +18,6 @@ package org.wso2.carbon.identity.provisioning; -import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.context.PrivilegedCarbonContext; @@ -40,6 +39,7 @@ public class ProvisioningThread implements Callable { private String connectorType; private String idPName; private CacheBackedProvisioningMgtDAO dao; + private boolean jitProvisioningEnabledForIdP; private static final Log log = LogFactory.getLog(ProvisioningThread.class); public ProvisioningThread(ProvisioningEntity provisioningEntity, String tenantDomainName, @@ -63,6 +63,28 @@ public ProvisioningThread(ProvisioningEntity provisioningEntity, String spTenant this.provisioningEntityTenantDomainName = provisioningEntityTenantDomainName; } + /** + * Parameterized constructor to propagate JIT provisioning state to the connector. + * + * @param provisioningEntity Provisioning entity containing details of the provisioning operation. + * @param spTenantDomainName Service provider tenant domain name. + * @param provisioningEntityTenantDomainName Tenant domain name of the provisioning entity. + * @param connector Outbound provisioning connector to perform the provisioning operation. + * @param connectorType Type of the outbound provisioning connector. + * @param idPName Name of the identity provider through which provisioning is triggered. + * @param dao Data access object to store and manage provisioning entity identifiers. + * @param jitProvisioningEnabledForIdP Flag indicating whether JIT provisioning is enabled or not. + */ + public ProvisioningThread(ProvisioningEntity provisioningEntity, String spTenantDomainName, + String provisioningEntityTenantDomainName, + AbstractOutboundProvisioningConnector connector, String connectorType, String idPName, + CacheBackedProvisioningMgtDAO dao, boolean jitProvisioningEnabledForIdP) { + + this(provisioningEntity, spTenantDomainName, provisioningEntityTenantDomainName, connector, connectorType, + idPName, dao); + this.jitProvisioningEnabledForIdP = jitProvisioningEnabledForIdP; + } + @Override public Boolean call() throws IdentityProvisioningException { @@ -83,7 +105,13 @@ public Boolean call() throws IdentityProvisioningException { /* Skip outbound provisioning triggered for JIT provisioning flow, where the JIT outbound is disabled for the configured connector. */ - if (provisioningEntity.isJitProvisioning() && !connector.isJitProvisioningEnabled()) { + if (provisioningEntity.isJitProvisioning() && !jitProvisioningEnabledForIdP) { + if (log.isDebugEnabled()) { + log.debug(String.format("Skipping outbound provisioning for entity: %s via IDP: %s, connector: " + + "%s. Reason: JIT provisioning is not enabled for this provisioning connector.", + ProvisioningUtil.maskIfRequired(provisioningEntity.getEntityName()), idPName, + connectorType)); + } return true; } ProvisionedIdentifier provisionedIdentifier = null; @@ -118,8 +146,10 @@ public Boolean call() throws IdentityProvisioningException { } success = true; } catch (Exception e) { - String errMsg = "Fail the Provisioning for Entity " + provisioningEntity.getEntityName() + - " For operation = " + provisioningEntity.getOperation(); + String maskedEntityName = ProvisioningUtil.maskIfRequired(provisioningEntity.getEntityName()); + String errMsg = "Outbound provisioning failed for connection: " + idPName + ", connector: " + connectorType + + ", entity: " + maskedEntityName + ", entity type: " + provisioningEntity.getEntityType() + + ", operation: " + provisioningEntity.getOperation(); log.warn(errMsg); throw new IdentityProvisioningException(errMsg, e); } finally { diff --git a/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/ProvisioningUtil.java b/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/ProvisioningUtil.java index ac23f53c1261..77c4bb52f262 100644 --- a/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/ProvisioningUtil.java +++ b/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/ProvisioningUtil.java @@ -30,6 +30,7 @@ import org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants; import org.wso2.carbon.identity.application.mgt.ApplicationConstants; import org.wso2.carbon.identity.application.mgt.ApplicationManagementService; +import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils; import org.wso2.carbon.identity.claim.metadata.mgt.ClaimMetadataHandler; import org.wso2.carbon.identity.core.util.IdentityUtil; @@ -618,4 +619,15 @@ public static boolean isApplicationBasedOutboundProvisioningEnabled() { } return applicationBasedOutboundProvisioningEnabled; } + + /** + * Mask the given value if it is required. + * + * @param value Value to be masked. + * @return Masked/unmasked value. + */ + public static String maskIfRequired(String value) { + + return LoggerUtils.isLogMaskingEnable ? LoggerUtils.getMaskedContent(value) : value; + } } diff --git a/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/RuntimeProvisioningConfig.java b/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/RuntimeProvisioningConfig.java index a3d9b2798d0b..87acdd7cc7cb 100644 --- a/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/RuntimeProvisioningConfig.java +++ b/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/RuntimeProvisioningConfig.java @@ -1,12 +1,12 @@ /* - * Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2014-2026, WSO2 LLC. (http://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. 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 + * 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 @@ -27,6 +27,7 @@ public class RuntimeProvisioningConfig implements Serializable { private boolean blocking; private boolean policyEnabled; + private boolean jitProvisioningEnabled; private Entry provisioningConnectorEntry; /** @@ -43,6 +44,24 @@ public void setBlocking(boolean blocking) { this.blocking = blocking; } + /** + * Get JIT provisioning enabled or not. + * @return JIT provisioning enabled or not. + */ + public boolean isJitProvisioningEnabled() { + + return jitProvisioningEnabled; + } + + /** + * Set JIT provisioning enabled or not. + * @param jitProvisioningEnabled JIT provisioning enabled or not. + */ + public void setJitProvisioningEnabled(boolean jitProvisioningEnabled) { + + this.jitProvisioningEnabled = jitProvisioningEnabled; + } + /** * @return */ diff --git a/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/cache/ServiceProviderProvisioningConnectorCache.java b/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/cache/ServiceProviderProvisioningConnectorCache.java index b6a6a748f80b..25e0132a92cd 100644 --- a/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/cache/ServiceProviderProvisioningConnectorCache.java +++ b/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/cache/ServiceProviderProvisioningConnectorCache.java @@ -35,7 +35,7 @@ private ServiceProviderProvisioningConnectorCache() { */ public static ServiceProviderProvisioningConnectorCache getInstance() { if (instance == null) { - synchronized (ProvisioningConnectorCache.class) { + synchronized (ServiceProviderProvisioningConnectorCache.class) { if (instance == null) { instance = new ServiceProviderProvisioningConnectorCache(); } diff --git a/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/listener/DefaultInboundUserProvisioningListener.java b/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/listener/DefaultInboundUserProvisioningListener.java index a3677efc5ce5..4da9f52e0b48 100644 --- a/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/listener/DefaultInboundUserProvisioningListener.java +++ b/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/listener/DefaultInboundUserProvisioningListener.java @@ -270,12 +270,12 @@ public boolean doPostUpdateUserListOfRole(String roleName, String[] deletedUsers if (threadLocalServiceProvider != null) { serviceProviderIdentifier = threadLocalServiceProvider.getServiceProviderName(); - tenantDomainName = threadLocalServiceProvider.getTenantDomain(); if (threadLocalServiceProvider.getServiceProviderType() == ProvisioningServiceProviderType.OAUTH) { serviceProviderIdentifier = ApplicationManagementService.getInstance() .getServiceProviderNameByClientId( serviceProviderIdentifier, - IdentityApplicationConstants.OAuth2.NAME, tenantDomainName); + IdentityApplicationConstants.OAuth2.NAME, + threadLocalServiceProvider.getTenantDomain()); } } @@ -570,13 +570,13 @@ private boolean provisionEntity(ProvisioningEntity provisioningEntity, String te if (threadLocalServiceProvider != null) { String serviceProvider = threadLocalServiceProvider.getServiceProviderName(); - tenantDomainName = threadLocalServiceProvider.getTenantDomain(); if (threadLocalServiceProvider.getServiceProviderType() == ProvisioningServiceProviderType.OAUTH) { try { serviceProvider = ApplicationManagementService.getInstance() .getServiceProviderNameByClientId( threadLocalServiceProvider.getServiceProviderName(), - IdentityApplicationConstants.OAuth2.NAME, tenantDomainName); + IdentityApplicationConstants.OAuth2.NAME, + threadLocalServiceProvider.getTenantDomain()); } catch (IdentityApplicationManagementException e) { log.error("Error while provisioning", e); return true; diff --git a/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/listener/ProvisioningErrorListener.java b/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/listener/ProvisioningErrorListener.java index 7a343b0d1781..33d9bc2f852a 100644 --- a/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/listener/ProvisioningErrorListener.java +++ b/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/listener/ProvisioningErrorListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) + * Copyright (c) 2022-2026, WSO2 Inc. (http://www.wso2.org) * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -296,13 +296,13 @@ private boolean outboundProvisionEntity(ProvisioningEntity provisioningEntity) t if (threadLocalServiceProvider != null) { String serviceProvider = threadLocalServiceProvider.getServiceProviderName(); - tenantDomainName = threadLocalServiceProvider.getTenantDomain(); if (threadLocalServiceProvider.getServiceProviderType() == ProvisioningServiceProviderType.OAUTH) { try { serviceProvider = ApplicationManagementService.getInstance() .getServiceProviderNameByClientId( threadLocalServiceProvider.getServiceProviderName(), - IdentityApplicationConstants.OAuth2.NAME, tenantDomainName); + IdentityApplicationConstants.OAuth2.NAME, + threadLocalServiceProvider.getTenantDomain()); } catch (IdentityApplicationManagementException e) { log.error("Error while provisioning", e); return true; diff --git a/components/provisioning/org.wso2.carbon.identity.provisioning/src/test/java/org/wso2/carbon/identity/provisioning/ProvisioningThreadTest.java b/components/provisioning/org.wso2.carbon.identity.provisioning/src/test/java/org/wso2/carbon/identity/provisioning/ProvisioningThreadTest.java index fa64002e4c28..a4de378c4be0 100644 --- a/components/provisioning/org.wso2.carbon.identity.provisioning/src/test/java/org/wso2/carbon/identity/provisioning/ProvisioningThreadTest.java +++ b/components/provisioning/org.wso2.carbon.identity.provisioning/src/test/java/org/wso2/carbon/identity/provisioning/ProvisioningThreadTest.java @@ -1,12 +1,12 @@ /* - * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2017-2026, WSO2 LLC. (http://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. 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 + * 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 @@ -39,10 +39,13 @@ import java.util.List; import java.util.Map; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; import static org.wso2.carbon.identity.provisioning.ProvisioningEntityType.GROUP; @@ -95,14 +98,13 @@ public void testCall(Object entityType, Object entityOperation, String tenantDom provisioningEntity = new ProvisioningEntity((ProvisioningEntityType) entityType, (ProvisioningOperation) entityOperation, attributeMap); provisioningEntity.setJitProvisioning(true); - when(mockConnector.isJitProvisioningEnabled()).thenReturn(true); CacheBackedProvisioningMgtDAO mockCacheBackedProvisioningMgDAO = mock(CacheBackedProvisioningMgtDAO.class); mockCarbonContext(privilegedCarbonContext); doNothing().when(mockCacheBackedProvisioningMgDAO).addProvisioningEntity(idPName, connectorType, provisioningEntity, -1234, tenantDomainName); ProvisioningThread provisioningThread = - new ProvisioningThread(provisioningEntity, tenantDomainName, mockConnector, connectorType, - idPName, mockCacheBackedProvisioningMgDAO); + new ProvisioningThread(provisioningEntity, tenantDomainName, null, mockConnector, connectorType, + idPName, mockCacheBackedProvisioningMgDAO, true); Boolean result = provisioningThread.call(); Assert.assertTrue(result); @@ -123,15 +125,15 @@ public void testCallForInvalidTenant() CacheBackedProvisioningMgtDAO cacheBackedProvisioningMgtDAO = mockCacheBackedProvisioningMgtDAO(privilegedCarbonContext, provisioningEntityCache); ProvisioningThread provisioningThread = - new ProvisioningThread(provisioningEntity, "", mockConnector, connectorType, - idPName, cacheBackedProvisioningMgtDAO); + new ProvisioningThread(provisioningEntity, "", null, mockConnector, connectorType, + idPName, cacheBackedProvisioningMgtDAO, false); provisioningThread.call(); } } @Test - public void testJITProvisionFlowWithJitOutboundDisabledConnector() throws IdentityProvisioningException { + public void testJITProvisionFlowWithJitOutboundDisabledConnector() throws Exception { System.setProperty("carbon.home", ""); try (MockedStatic privilegedCarbonContext = mockStatic(PrivilegedCarbonContext.class); @@ -143,12 +145,34 @@ public void testJITProvisionFlowWithJitOutboundDisabledConnector() throws Identi CacheBackedProvisioningMgtDAO cacheBackedProvisioningMgtDAO = mockCacheBackedProvisioningMgtDAO(privilegedCarbonContext, provisioningEntityCache); ProvisioningThread provisioningThread = - new ProvisioningThread(provisioningEntity, "", mockConnector, connectorType, - idPName, cacheBackedProvisioningMgtDAO); + new ProvisioningThread(provisioningEntity, "", null, mockConnector, connectorType, + idPName, cacheBackedProvisioningMgtDAO, false); - when(mockConnector.isJitProvisioningEnabled()).thenReturn(false); Boolean result = provisioningThread.call(); Assert.assertTrue(result); + // The thread must short-circuit before reaching the connector. + verify(mockConnector, never()).provision(any()); + } + } + + @Test + public void testNonJitRequestProceedsWhenJitDisabledForIdP() throws Exception { + + System.setProperty("carbon.home", ""); + try (MockedStatic privilegedCarbonContext = mockStatic(PrivilegedCarbonContext.class)) { + + provisioningEntity = new ProvisioningEntity(USER, POST, null); + provisioningEntity.setJitProvisioning(false); + CacheBackedProvisioningMgtDAO mockDao = mock(CacheBackedProvisioningMgtDAO.class); + mockCarbonContext(privilegedCarbonContext); + // jitProvisioningEnabledForIdP=false must not block regular provisioning. + ProvisioningThread provisioningThread = + new ProvisioningThread(provisioningEntity, tenantDomainName, null, mockConnector, connectorType, + idPName, mockDao, false); + + Boolean result = provisioningThread.call(); + Assert.assertTrue(result); + verify(mockConnector).provision(provisioningEntity); } } diff --git a/features/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt.server.feature/resources/api-resource-collection.xml b/features/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt.server.feature/resources/api-resource-collection.xml index afcd2c298dc5..9c1e01cac1a6 100644 --- a/features/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt.server.feature/resources/api-resource-collection.xml +++ b/features/api-resource-mgt/org.wso2.carbon.identity.api.resource.mgt.server.feature/resources/api-resource-collection.xml @@ -1,6 +1,6 @@