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 @@