-
Notifications
You must be signed in to change notification settings - Fork 608
Enhance outbound provisioning with resident SP fallback and sub-organization support #7765
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
52a92f0
4094c56
a7450fe
eced60b
cf35eb8
a05ce0c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -204,24 +204,17 @@ private Map<String, RuntimeProvisioningConfig> 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,15 +243,14 @@ private Map<String, RuntimeProvisioningConfig> getOutboundProvisioningConnectors | |
| * @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) | ||
| 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; | ||
| } | ||
PasinduYeshan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
@@ -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<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
|
||
| ServiceProvider localSP = ApplicationManagementService.getInstance() | ||
|
Comment on lines
+385
to
+389
|
||
| .getServiceProvider(LOCAL_SP, spTenantDomainName); | ||
| if (localSP != null) { | ||
PasinduYeshan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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<Boolean> 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<Boolean> 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<Boolean> 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; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JIT enablement is derived from a potentially stale IdP object.
enableJitProvisioningis computed fromfIdPbefore the enabled IdP is re-resolved ingetOutboundProvisioningConnector(...). This can propagate outdated JIT state intoRuntimeProvisioningConfigand cause incorrect per-IdP behavior.Suggested direction
🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is intentional. At this point, the fIdP is created using the application's configured outbound JIT provisioning settings. getOutboundProvisioningConnector fetches an IdP-level JIT enablement check, which is not relevant in the context of outbound provisioning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@PasinduYeshan, thank you for the clarification! That makes perfect sense—the application's outbound provisioning configuration should take precedence for JIT settings in the outbound flow, rather than the IdP's general JIT configuration. I appreciate you explaining the architectural intent here.