Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>org.wso2.carbon.identity.application.authentication.framework</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.identity.framework</groupId>
<artifactId>org.wso2.carbon.identity.central.log.mgt</artifactId>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
Expand Down Expand Up @@ -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}",
</Import-Package>
<Export-Package>
!org.wso2.carbon.identity.provisioning.internal,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,9 +357,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;
}
Expand Down Expand Up @@ -393,6 +391,21 @@ public void provision(ProvisioningEntity provisioningEntity, String serviceProvi
Map<String, RuntimeProvisioningConfig> 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)) {
Comment on lines +385 to +388
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new LOCAL_SP fallback path (when application-based outbound provisioning is disabled and an app has no connectors) introduces important behavior changes, but there is no corresponding test coverage in the provisioning module tests. Please add a unit/integration test that exercises this fallback (connectors empty for app, LOCAL_SP connectors present) and asserts the effective connectors/dialect/tenant flow used.

Copilot uses AI. Check for mistakes.
ServiceProvider localSP = ApplicationManagementService.getInstance()
Comment on lines +385 to +389
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When falling back to LOCAL_SP you replace serviceProvider (and related dialect/claim-mapping decisions), but provisioningEntityTenantDomainName was computed earlier based on the original service provider’s isSaasApp() value. This can cause the provisioning thread to pick the wrong tenant flow after the fallback. Consider recomputing provisioningEntityTenantDomainName (and any other SP-derived variables) after selecting the final serviceProvider to use.

Copilot uses AI. Check for mistakes.
.getServiceProvider(LOCAL_SP, spTenantDomainName);
if (localSP != null) {
serviceProvider = localSP;
connectors = getOutboundProvisioningConnectors(localSP, spTenantDomainName);
inboundClaimDialect = IdentityProvisioningConstants.WSO2_CARBON_DIALECT;
// LOCAL_SP uses WSO2_CARBON_DIALECT; spClaimMappings is not needed.
spClaimMappings = null;
}
}

ProvisioningEntity outboundProEntity;

ExecutorService executors = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -118,8 +117,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 IDP: " + idPName + ", connector: " + connectorType
+ ", entity: " + maskedEntityName + ", entity type: " + provisioningEntity.getEntityType()
+ ", operation: " + provisioningEntity.getOperation();
log.warn(errMsg);
throw new IdentityProvisioningException(errMsg, e);
} finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}

Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Comment on lines 1 to 5
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

License header does not match the required WSO2 Apache-2.0 header format (company name/URL/text) expected for Java files. Please replace this header with the standard 2026 WSO2 LLC Apache 2.0 header used in this repo to ensure license compliance consistency.

Copilot generated this review using guidance from repository custom instructions.
Expand Down Expand Up @@ -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;
Expand Down
Loading