From d4948efa1365624dd80d7c2c47405f4512a250b6 Mon Sep 17 00:00:00 2001 From: sadilchamishka Date: Thu, 5 Sep 2024 08:24:38 +0530 Subject: [PATCH 1/4] Role based user provisioning improvements --- .../pom.xml | 12 ++ .../OutboundProvisioningManager.java | 65 +++++++-- .../provisioning/ProvisioningUtil.java | 14 ++ .../IdentityProvisionServiceComponent.java | 29 +++- .../ProvisioningServiceDataHolder.java | 25 ++++ ...efaultInboundUserProvisioningListener.java | 7 +- .../listener/ProvisioningRoleMgtListener.java | 124 ++++++++++++++++++ 7 files changed, 266 insertions(+), 10 deletions(-) create mode 100644 components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/listener/ProvisioningRoleMgtListener.java diff --git a/components/provisioning/org.wso2.carbon.identity.provisioning/pom.xml b/components/provisioning/org.wso2.carbon.identity.provisioning/pom.xml index 675b12b33950..3d4b2117b32c 100644 --- a/components/provisioning/org.wso2.carbon.identity.provisioning/pom.xml +++ b/components/provisioning/org.wso2.carbon.identity.provisioning/pom.xml @@ -84,6 +84,14 @@ org.wso2.carbon.identity.framework org.wso2.carbon.identity.base + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.role.v2.mgt.core + + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.application.authentication.framework + org.wso2.balana org.wso2.balana.utils @@ -163,6 +171,10 @@ org.wso2.carbon.user.mgt.*; version="${carbon.identity.package.import.version.range}", org.json;version="${json.wso2.version.range}", + org.wso2.carbon.identity.role.v2.mgt.core.*; + 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.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 5020347474b8..5a7670ddc7ae 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 @@ -42,7 +42,11 @@ 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.identity.provisioning.internal.ProvisioningServiceDataHolder; import org.wso2.carbon.identity.provisioning.rules.XACMLBasedRuleHandler; +import org.wso2.carbon.identity.role.v2.mgt.core.RoleConstants; +import org.wso2.carbon.identity.role.v2.mgt.core.exception.IdentityRoleManagementException; +import org.wso2.carbon.identity.role.v2.mgt.core.model.RoleBasicInfo; import org.wso2.carbon.idp.mgt.IdentityProviderManagementException; import org.wso2.carbon.idp.mgt.IdentityProviderManager; import org.wso2.carbon.idp.mgt.util.IdPManagementUtil; @@ -51,6 +55,7 @@ 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.common.AbstractUserStoreManager; import org.wso2.carbon.user.core.service.RealmService; import org.wso2.carbon.user.core.util.UserCoreUtil; @@ -65,6 +70,7 @@ import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.stream.Collectors; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.CONSOLE_APPLICATION_NAME; import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.LOCAL_SP; @@ -73,6 +79,7 @@ import static org.wso2.carbon.identity.provisioning.IdentityProvisioningConstants.SELF_SIGNUP_ROLE; import static org.wso2.carbon.identity.provisioning.ProvisioningUtil.isApplicationBasedOutboundProvisioningEnabled; import static org.wso2.carbon.identity.provisioning.ProvisioningUtil.isUserTenantBasedOutboundProvisioningEnabled; +import static org.wso2.carbon.identity.role.mgt.core.RoleConstants.INTERNAL_DOMAIN; /** * @@ -494,9 +501,15 @@ public void provision(ProvisioningEntity provisioningEntity, String serviceProvi List newUsersList = attributes.get(ClaimMapping.build( IdentityProvisioningConstants.NEW_USER_CLAIM_URI, null, null, false)); + if (newUsersList == null) { + newUsersList = new ArrayList<>(); + } List deletedUsersList = attributes.get(ClaimMapping.build( IdentityProvisioningConstants.DELETED_USER_CLAIM_URI, null, null, false)); + if (deletedUsersList == null) { + deletedUsersList = new ArrayList<>(); + } Map> mappedUserClaims; ProvisionedIdentifier provisionedUserIdentifier; @@ -679,10 +692,10 @@ private ProvisioningEntity getInboundProvisioningEntity(ProvisioningEntity provi IdentityProvisioningConstants.USERNAME_CLAIM_URI, null, null, false), Arrays.asList(new String[]{userName})); } - List roleListOfUser = getUserRoles(userName, tenantDomain); - if (roleListOfUser != null) { + List groupListOfUser = getUserGroups(userName, tenantDomain); + if (groupListOfUser != null) { outboundAttributes.put(ClaimMapping.build( - GROUP_CLAIM_URI, null, null, false), roleListOfUser); + GROUP_CLAIM_URI, null, null, false), groupListOfUser); } String domainAwareName = userName; @@ -873,14 +886,20 @@ protected boolean canUserBeProvisioned(ProvisioningEntity provisioningEntity, String userName = provisioningEntity.getEntityName(); List provisioningRoleList = Arrays.asList(provisionByRoleList); + /* Both user groups and roles are considered when deciding to outbound provisioning. + The roles have "Internal/" prefix compared to the groups. */ + List roleGroupListOfUser = new ArrayList<>(); + List groupListOfUser = getUserGroups(userName, tenantDomain); List roleListOfUser = getUserRoles(userName, tenantDomain); - if (userHasProvisioningRoles(roleListOfUser, provisioningRoleList, userName)) { + roleGroupListOfUser.addAll(roleListOfUser); + roleGroupListOfUser.addAll(groupListOfUser); + if (userHasProvisioningRoles(roleGroupListOfUser, provisioningRoleList, userName)) { return true; } - List newRoleListOfUser = provisioningEntity.getAttributes().get(ClaimMapping.build + List newGroupListOfUser = provisioningEntity.getAttributes().get(ClaimMapping.build (GROUP_CLAIM_URI, null, null, false)); - if (userHasProvisioningRoles(newRoleListOfUser, provisioningRoleList, userName)) { + if (userHasProvisioningRoles(newGroupListOfUser, provisioningRoleList, userName)) { return true; } } @@ -913,7 +932,7 @@ protected boolean canUserBeDeProvisioned(ProvisionedIdentifier provisionedIdenti * @throws CarbonException * @throws UserStoreException */ - private List getUserRoles(String userName, String tenantDomain) throws UserStoreException { + private List getUserGroups(String userName, String tenantDomain) throws UserStoreException { RealmService realmService = IdentityProvisionServiceComponent.getRealmService(); int tenantId = realmService.getTenantManager().getTenantId(tenantDomain); @@ -922,7 +941,37 @@ private List getUserRoles(String userName, String tenantDomain) throws U UserStoreManager userstore = realm.getUserStoreManager(); String[] newRoles = userstore.getRoleListOfUser(userName); - return Arrays.asList(newRoles); + return Arrays.stream(newRoles).filter(role -> !role.startsWith(INTERNAL_DOMAIN)).collect(Collectors.toList()); + } + + private List getUserRoles(String username, String tenantDomain) throws UserStoreException { + + List userRoleList = new ArrayList<>(); + RealmService realmService = IdentityProvisionServiceComponent.getRealmService(); + int tenantId = realmService.getTenantManager().getTenantId(tenantDomain); + UserRealm realm = (UserRealm) realmService.getTenantUserRealm(tenantId); + AbstractUserStoreManager userStoreManager = (AbstractUserStoreManager) realm.getUserStoreManager(); + String userId = userStoreManager.getUserIDFromUserName(username); + if (userId == null) { + return userRoleList; + } + try { + List userRoleIdList = ProvisioningServiceDataHolder.getInstance().getRoleManagementService() + .getRoleIdListOfUser(userId, tenantDomain); + for (String roleId : userRoleIdList) { + RoleBasicInfo roleBasicInfo = ProvisioningServiceDataHolder.getInstance().getRoleManagementService() + .getRoleBasicInfoById(roleId, tenantDomain); + // Only organization audience roles are supported for role based outbound provisioning. + if (!RoleConstants.ORGANIZATION.equals(roleBasicInfo.getAudience())) { + continue; + } + userRoleList.add(roleBasicInfo.getName()); + } + } catch (IdentityRoleManagementException e) { + throw new UserStoreException(e.getMessage(), e); + } + String internalRolePrefix = RoleConstants.INTERNAL_DOMAIN + UserCoreConstants.DOMAIN_SEPARATOR; + return userRoleList.stream().map(role -> internalRolePrefix + role).collect(Collectors.toList()); } /** 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 17e8bc1eb36a..19a9db7d0a7f 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 @@ -28,6 +28,7 @@ import org.wso2.carbon.identity.application.common.model.ServiceProvider; import org.wso2.carbon.identity.application.common.model.OutboundProvisioningConfig; 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.claim.metadata.mgt.ClaimMetadataHandler; import org.wso2.carbon.identity.core.util.IdentityUtil; @@ -560,6 +561,14 @@ public static boolean isUserTenantBasedOutboundProvisioningEnabled() { public static boolean isOutboundProvisioningEnabled(String serviceProviderIdentifier, String tenantDomainName) throws IdentityApplicationManagementException { + /* Outbound provisioning is enabled for organization by default. If application bound provisioning enabled, + each application should configure outbound provisioning. + */ + + if (!isApplicationBasedOutboundProvisioningEnabled()) { + return true; + } + ServiceProvider serviceProvider = ApplicationManagementService.getInstance() .getServiceProvider(serviceProviderIdentifier, tenantDomainName); @@ -568,6 +577,11 @@ public static boolean isOutboundProvisioningEnabled(String serviceProviderIdenti serviceProviderIdentifier); } + // The console app not required to enable outbound provisioning. + if (ApplicationConstants.CONSOLE_APPLICATION_NAME.equals(serviceProvider.getApplicationName())) { + return true; + } + OutboundProvisioningConfig outboundProvisioningConfiguration = serviceProvider .getOutboundProvisioningConfig(); diff --git a/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/internal/IdentityProvisionServiceComponent.java b/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/internal/IdentityProvisionServiceComponent.java index 129a0e27bd46..50c70f55a3f6 100644 --- a/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/internal/IdentityProvisionServiceComponent.java +++ b/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/internal/IdentityProvisionServiceComponent.java @@ -36,6 +36,9 @@ import org.wso2.carbon.identity.provisioning.listener.ProvisioningApplicationMgtListener; import org.wso2.carbon.identity.provisioning.listener.ProvisioningErrorListener; import org.wso2.carbon.identity.provisioning.listener.ProvisioningIdentityProviderMgtListener; +import org.wso2.carbon.identity.provisioning.listener.ProvisioningRoleMgtListener; +import org.wso2.carbon.identity.role.v2.mgt.core.RoleManagementService; +import org.wso2.carbon.identity.role.v2.mgt.core.listener.RoleManagementListener; import org.wso2.carbon.idp.mgt.listener.IdentityProviderMgtListener; import org.wso2.carbon.user.core.listener.UserManagementErrorEventListener; import org.wso2.carbon.user.core.listener.UserOperationEventListener; @@ -88,7 +91,10 @@ public static Map getConnectorFact protected void activate(ComponentContext context) { try { ProvisioningServiceDataHolder.getInstance().setBundleContext(context.getBundleContext()); - ProvisioningServiceDataHolder.getInstance().getBundleContext().registerService(UserOperationEventListener.class.getName(), new DefaultInboundUserProvisioningListener(), null); + DefaultInboundUserProvisioningListener provisioningListener = new DefaultInboundUserProvisioningListener(); + ProvisioningServiceDataHolder.getInstance().setDefaultInboundUserProvisioningListener(provisioningListener); + ProvisioningServiceDataHolder.getInstance().getBundleContext() + .registerService(UserOperationEventListener.class.getName(), provisioningListener, null); if (log.isDebugEnabled()) { log.debug("Identity Provision Event listener registered successfully"); } @@ -103,6 +109,9 @@ protected void activate(ComponentContext context) { ProvisioningServiceDataHolder.getInstance().getBundleContext() .registerService(UserManagementErrorEventListener.class.getName(), new ProvisioningErrorListener(), null); + ProvisioningServiceDataHolder.getInstance().getBundleContext() + .registerService(RoleManagementListener.class, new ProvisioningRoleMgtListener(), null); + log.debug("Provisioning role management listener registered successfully"); if (log.isDebugEnabled()) { log.debug("Identity provisioning error event listener registered successfully"); } @@ -207,5 +216,23 @@ protected void unsetRolePermissionManagementService(RolePermissionManagementServ } ProvisioningServiceDataHolder.getInstance().setRolePermissionManagementService(null); } + + @Reference( + name = "org.wso2.carbon.identity.role.v2.mgt.core.RoleManagementService", + service = org.wso2.carbon.identity.role.v2.mgt.core.RoleManagementService.class, + cardinality = ReferenceCardinality.MANDATORY, + policy = ReferencePolicy.DYNAMIC, + unbind = "unsetRoleManagementServiceV2") + protected void setRoleManagementServiceV2(RoleManagementService roleManagementService) { + + ProvisioningServiceDataHolder.getInstance().setRoleManagementService(roleManagementService); + log.debug("RoleManagementService set in ProvisioningServiceDataHolder bundle."); + } + + protected void unsetRoleManagementServiceV2(RoleManagementService roleManagementService) { + + ProvisioningServiceDataHolder.getInstance().setRoleManagementService(null); + log.debug("RoleManagementService unset in ProvisioningServiceDataHolder bundle."); + } } diff --git a/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/internal/ProvisioningServiceDataHolder.java b/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/internal/ProvisioningServiceDataHolder.java index ebf3311a37b3..ddd3d5562ba0 100644 --- a/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/internal/ProvisioningServiceDataHolder.java +++ b/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/internal/ProvisioningServiceDataHolder.java @@ -21,6 +21,8 @@ import org.osgi.framework.BundleContext; import org.wso2.carbon.identity.entitlement.EntitlementService; import org.wso2.carbon.identity.provisioning.AbstractProvisioningConnectorFactory; +import org.wso2.carbon.identity.provisioning.listener.DefaultInboundUserProvisioningListener; +import org.wso2.carbon.identity.role.v2.mgt.core.RoleManagementService; import org.wso2.carbon.user.core.service.RealmService; import org.wso2.carbon.user.mgt.RolePermissionManagementService; @@ -35,6 +37,8 @@ public class ProvisioningServiceDataHolder { private EntitlementService entitlementService; private RolePermissionManagementService rolePermissionManagementService; private Map connectorFactories = new HashMap(); + private DefaultInboundUserProvisioningListener defaultInboundUserProvisioningListener; + private RoleManagementService roleManagementService; private ProvisioningServiceDataHolder() { } @@ -94,6 +98,27 @@ public RolePermissionManagementService getRolePermissionManagementService() { } return rolePermissionManagementService; } + + public DefaultInboundUserProvisioningListener getDefaultInboundUserProvisioningListener() { + + return defaultInboundUserProvisioningListener; + } + + public void setDefaultInboundUserProvisioningListener( + DefaultInboundUserProvisioningListener defaultInboundUserProvisioningListener) { + + this.defaultInboundUserProvisioningListener = defaultInboundUserProvisioningListener; + } + + public RoleManagementService getRoleManagementService() { + + return roleManagementService; + } + + public void setRoleManagementService(RoleManagementService roleManagementService) { + + this.roleManagementService = roleManagementService; + } } 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 07e2c15848e1..1224f978cb66 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 @@ -39,6 +39,7 @@ import org.wso2.carbon.identity.provisioning.ProvisioningEntityType; import org.wso2.carbon.identity.provisioning.ProvisioningOperation; import org.wso2.carbon.identity.provisioning.ProvisioningUtil; +import org.wso2.carbon.identity.role.v2.mgt.core.util.RoleManagementUtils; import org.wso2.carbon.user.api.Permission; import org.wso2.carbon.user.core.UserStoreException; import org.wso2.carbon.user.core.UserStoreManager; @@ -435,7 +436,11 @@ public boolean doPostUpdateUserListOfRole(String roleName, String[] deletedUsers return true; } - String[] userList = userStoreManager.getUserListOfRole(roleName); + String[] userList = new String[0]; + // Passing all user list of the role is not used, hence we can remove this logic if unnecessary. + if (!RoleManagementUtils.isHybridRole(roleName)) { + userList = userStoreManager.getUserListOfRole(roleName); + } Map> outboundAttributes = new HashMap<>(); diff --git a/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/listener/ProvisioningRoleMgtListener.java b/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/listener/ProvisioningRoleMgtListener.java new file mode 100644 index 000000000000..affd743f1d12 --- /dev/null +++ b/components/provisioning/org.wso2.carbon.identity.provisioning/src/main/java/org/wso2/carbon/identity/provisioning/listener/ProvisioningRoleMgtListener.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * 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 + * + * 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.listener; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.identity.core.model.IdentityEventListenerConfig; +import org.wso2.carbon.identity.core.util.IdentityCoreConstants; +import org.wso2.carbon.identity.core.util.IdentityTenantUtil; +import org.wso2.carbon.identity.core.util.IdentityUtil; +import org.wso2.carbon.identity.provisioning.internal.ProvisioningServiceDataHolder; +import org.wso2.carbon.identity.role.v2.mgt.core.RoleConstants; +import org.wso2.carbon.identity.role.v2.mgt.core.exception.IdentityRoleManagementException; +import org.wso2.carbon.identity.role.v2.mgt.core.listener.AbstractRoleManagementListener; +import org.wso2.carbon.identity.role.v2.mgt.core.listener.RoleManagementListener; +import org.wso2.carbon.identity.role.v2.mgt.core.model.RoleBasicInfo; +import org.wso2.carbon.user.api.UserStoreException; +import org.wso2.carbon.user.core.UserCoreConstants; +import org.wso2.carbon.user.core.common.AbstractUserStoreManager; + +import java.util.ArrayList; +import java.util.List; + +/** + * The implementation for outbound user provisioning based on role. + */ +public class ProvisioningRoleMgtListener extends AbstractRoleManagementListener { + + private static final Log LOG = LogFactory.getLog(ProvisioningRoleMgtListener.class); + + @Override + public boolean isEnable() { + + IdentityEventListenerConfig identityEventListenerConfig = IdentityUtil.readEventListenerProperty + (RoleManagementListener.class.getName(), this.getClass().getName()); + if (identityEventListenerConfig == null) { + return true; + } + return Boolean.parseBoolean(identityEventListenerConfig.getEnable()); + } + + @Override + public int getExecutionOrderId() { + + IdentityEventListenerConfig identityEventListenerConfig = IdentityUtil.readEventListenerProperty + (RoleManagementListener.class.getName(), this.getClass().getName()); + if (identityEventListenerConfig == null) { + return IdentityCoreConstants.EVENT_LISTENER_ORDER_ID; + } + return identityEventListenerConfig.getOrder(); + } + + @Override + public int getDefaultOrderId() { + + IdentityEventListenerConfig identityEventListenerConfig = IdentityUtil.readEventListenerProperty + (RoleManagementListener.class.getName(), this.getClass().getName()); + if (identityEventListenerConfig == null) { + return IdentityCoreConstants.EVENT_LISTENER_ORDER_ID; + } + return identityEventListenerConfig.getOrder(); + } + + @Override + public void postUpdateUserListOfRole(String roleId, List newUserIDList, List deletedUserIDList, + String tenantDomain) throws IdentityRoleManagementException { + + try { + AbstractUserStoreManager userStoreManager = (AbstractUserStoreManager) ProvisioningServiceDataHolder + .getInstance().getRealmService().getTenantUserRealm(IdentityTenantUtil.getTenantId(tenantDomain)) + .getUserStoreManager(); + + String[] deletedUserNames = resolveUserNameFromUserIds(deletedUserIDList, userStoreManager); + + String[] newUserNames = resolveUserNameFromUserIds(newUserIDList, userStoreManager); + + if (deletedUserNames.length > 0 && newUserNames.length > 0) { + return; + } + + RoleBasicInfo roleBasicInfo = ProvisioningServiceDataHolder.getInstance().getRoleManagementService() + .getRoleBasicInfoById(roleId,tenantDomain); + // Only organization audience roles are supported for role based outbound provisioning + if (!RoleConstants.ORGANIZATION.equals(roleBasicInfo.getAudience())) { + return; + } + String roleName = RoleConstants.INTERNAL_DOMAIN + UserCoreConstants.DOMAIN_SEPARATOR + + roleBasicInfo.getName(); + ProvisioningServiceDataHolder.getInstance().getDefaultInboundUserProvisioningListener() + .doPostUpdateUserListOfRole(roleName, deletedUserNames, newUserNames, userStoreManager); + } catch (UserStoreException e) { + throw new IdentityRoleManagementException(e.getMessage(), e); + } + } + + private String[] resolveUserNameFromUserIds(List userIds, AbstractUserStoreManager userStoreManager) { + + List userNames = new ArrayList<>(); + for (String userID : userIds) { + try { + userNames.add(userStoreManager.getUserNameFromUserID(userID)); + } catch (org.wso2.carbon.user.core.UserStoreException e) { + LOG.warn("Fail to resolve the user by user-id."); + } + } + return userNames.toArray(new String[0]); + } +} From b6c4e34afe379b4e83ddfdaf2bac4488d1e96c7f Mon Sep 17 00:00:00 2001 From: sadilchamishka Date: Fri, 6 Sep 2024 10:41:35 +0530 Subject: [PATCH 2/4] Add inline comment --- .../identity/provisioning/OutboundProvisioningManager.java | 1 + 1 file changed, 1 insertion(+) 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 5a7670ddc7ae..850d73491eb9 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 @@ -890,6 +890,7 @@ protected boolean canUserBeProvisioned(ProvisioningEntity provisioningEntity, The roles have "Internal/" prefix compared to the groups. */ List roleGroupListOfUser = new ArrayList<>(); List groupListOfUser = getUserGroups(userName, tenantDomain); + // Only fetch directly assigned roles to the user. List roleListOfUser = getUserRoles(userName, tenantDomain); roleGroupListOfUser.addAll(roleListOfUser); roleGroupListOfUser.addAll(groupListOfUser); From c1a5e054b38d39011a2495ecc3bb287901552bc0 Mon Sep 17 00:00:00 2001 From: sadilchamishka Date: Tue, 10 Sep 2024 17:05:37 +0530 Subject: [PATCH 3/4] Add role based outbound provision related listener configs --- .../resources/identity.xml | 5 +++++ .../resources/identity.xml.j2 | 7 +++++++ ...g.wso2.carbon.identity.core.server.feature.default.json | 3 +++ 3 files changed, 15 insertions(+) diff --git a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml index 6c7a97933b6a..11da83661435 100644 --- a/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml +++ b/features/identity-core/org.wso2.carbon.identity.core.server.feature/resources/identity.xml @@ -1372,6 +1372,11 @@ name="org.wso2.carbon.identity.organization.management.organization.user.sharing.listener.SharedUserOperationEventListener" orderId="128" enable="true"/> + + + + Date: Tue, 10 Sep 2024 17:07:49 +0530 Subject: [PATCH 4/4] Code refactoring --- .../wso2/carbon/identity/provisioning/ProvisioningUtil.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 19a9db7d0a7f..ac23f53c1261 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 @@ -561,10 +561,10 @@ public static boolean isUserTenantBasedOutboundProvisioningEnabled() { public static boolean isOutboundProvisioningEnabled(String serviceProviderIdentifier, String tenantDomainName) throws IdentityApplicationManagementException { - /* Outbound provisioning is enabled for organization by default. If application bound provisioning enabled, + /* + Outbound provisioning is enabled for organization by default. If application bound provisioning enabled, each application should configure outbound provisioning. - */ - + */ if (!isApplicationBasedOutboundProvisioningEnabled()) { return true; }