From 8d9405ffce46ca53e2feba8da7322a7bc0920a08 Mon Sep 17 00:00:00 2001 From: AnuradhaSK Date: Fri, 20 Oct 2023 18:31:27 +0530 Subject: [PATCH] update shared role management on app associated role update --- .../pom.xml | 5 +- .../handler/SharedRoleMgtHandler.java | 143 +----- .../listener/SharedRoleMgtListener.java | 436 +++++++++++++++++- 3 files changed, 429 insertions(+), 155 deletions(-) diff --git a/components/org.wso2.carbon.identity.organization.management.handler/pom.xml b/components/org.wso2.carbon.identity.organization.management.handler/pom.xml index 608618923..d7e9f1b50 100644 --- a/components/org.wso2.carbon.identity.organization.management.handler/pom.xml +++ b/components/org.wso2.carbon.identity.organization.management.handler/pom.xml @@ -68,7 +68,10 @@ org.wso2.carbon.identity.framework org.wso2.carbon.identity.role.v2.mgt.core - + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.application.mgt + org.testng diff --git a/components/org.wso2.carbon.identity.organization.management.handler/src/main/java/org/wso2/carbon/identity/organization/management/handler/SharedRoleMgtHandler.java b/components/org.wso2.carbon.identity.organization.management.handler/src/main/java/org/wso2/carbon/identity/organization/management/handler/SharedRoleMgtHandler.java index ff2c66a3d..d47f38cb6 100644 --- a/components/org.wso2.carbon.identity.organization.management.handler/src/main/java/org/wso2/carbon/identity/organization/management/handler/SharedRoleMgtHandler.java +++ b/components/org.wso2.carbon.identity.organization.management.handler/src/main/java/org/wso2/carbon/identity/organization/management/handler/SharedRoleMgtHandler.java @@ -20,22 +20,15 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException; -import org.wso2.carbon.identity.application.mgt.ApplicationManagementService; import org.wso2.carbon.identity.event.IdentityEventConstants; import org.wso2.carbon.identity.event.IdentityEventException; import org.wso2.carbon.identity.event.event.Event; import org.wso2.carbon.identity.event.handler.AbstractEventHandler; import org.wso2.carbon.identity.organization.management.application.OrgApplicationManager; -import org.wso2.carbon.identity.organization.management.application.constant.OrgApplicationMgtConstants; import org.wso2.carbon.identity.organization.management.application.model.SharedApplication; -import org.wso2.carbon.identity.organization.management.ext.Constants; import org.wso2.carbon.identity.organization.management.handler.internal.OrganizationManagementHandlerDataHolder; import org.wso2.carbon.identity.organization.management.service.OrganizationManager; import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException; -import org.wso2.carbon.identity.organization.management.service.model.BasicOrganization; -import org.wso2.carbon.identity.organization.management.service.model.Organization; -import org.wso2.carbon.identity.organization.management.service.model.ParentOrganizationDO; import org.wso2.carbon.identity.organization.management.service.util.OrganizationManagementUtil; import org.wso2.carbon.identity.role.v2.mgt.core.IdentityRoleManagementException; import org.wso2.carbon.identity.role.v2.mgt.core.RoleBasicInfo; @@ -63,14 +56,6 @@ public void handleEvent(Event event) throws IdentityEventException { String eventName = event.getEventName(); Map eventProperties = event.getEventProperties(); switch (eventName) { - case OrgApplicationMgtConstants.EVENT_POST_SHARE_APPLICATION: - /* - If the main application use application audienced roles, create the role for shared app's org space, - and add the relationship. If the main application use organization audienced roles, create the role in - shared app's org space, and add the relationship if already not exists. - */ - createOrganizationRolesOnAppSharing(eventProperties); - break; case IdentityEventConstants.Event.POST_ADD_ROLE_V2_EVENT: createOrganizationRolesOnNewRoleCreation(eventProperties); break; @@ -82,41 +67,6 @@ public void handleEvent(Event event) throws IdentityEventException { } } - private void createOrganizationRolesOnNewOrgCreation(Map eventProperties) - throws IdentityEventException { - - try { - Organization organization = (Organization) eventProperties.get(Constants.EVENT_PROP_ORGANIZATION); - String organizationId = organization.getId(); - if (getOrganizationManager().isPrimaryOrganization(organizationId)) { - return; - } - String subOrgTenantDomain = getOrganizationManager().resolveTenantDomain(organizationId); - ParentOrganizationDO parentOrg = organization.getParent(); - String parentOrgId = parentOrg.getId(); - // Get parent organization's roles which has organization audience. - String filter = RoleConstants.AUDIENCE_ID + " " + RoleConstants.EQ + " " + parentOrg.getId(); - String parenTenantDomain = getOrganizationManager().resolveTenantDomain(parentOrgId); - List parentOrgRoles = - getRoleManagementServiceV2().getRoles(filter, null, 0, null, null, parenTenantDomain); - for (RoleBasicInfo parentOrgRole : parentOrgRoles) { - String parentOrgRoleName = parentOrgRole.getName(); - // Create the role in the sub org. - RoleBasicInfo subOrgRole = - getRoleManagementServiceV2().addRole(parentOrgRoleName, Collections.emptyList(), - Collections.emptyList(), Collections.emptyList(), RoleConstants.ORGANIZATION, - organizationId, subOrgTenantDomain); - // Add relationship between parent org role and sub org role. - getRoleManagementServiceV2().addMainRoleToSharedRoleRelationship(parentOrgRole.getId(), - subOrgRole.getId(), parenTenantDomain, subOrgTenantDomain); - } - } catch (OrganizationManagementException e) { - throw new IdentityEventException("Error occurred while resolving organization id from tenant domain.", e); - } catch (IdentityRoleManagementException e) { - throw new IdentityEventException("Error occurred while adding main role to shared role relationship.", e); - } - } - private void createOrganizationRolesOnNewRoleCreation(Map eventProperties) throws IdentityEventException { @@ -139,10 +89,9 @@ private void createOrganizationRolesOnNewRoleCreation(Map eventP List sharedApplications = getOrgApplicationManager().getSharedApplications(roleOrgId, roleAudienceId); int noOfSharedApps = sharedApplications.size(); - CompletableFuture[] creations = new CompletableFuture[noOfSharedApps]; for (int i = 0; i < noOfSharedApps; i++) { final int taskId = i; - CompletableFuture sharedRoleCreation = CompletableFuture.runAsync(() -> { + CompletableFuture.runAsync(() -> { try { String sharedApplicationId = sharedApplications.get(taskId).getSharedApplicationId(); String sharedOrganizationId = sharedApplications.get(taskId).getOrganizationId(); @@ -160,93 +109,26 @@ private void createOrganizationRolesOnNewRoleCreation(Map eventP LOG.error("Error occurred while creating shared role in organization with id: " + sharedApplications.get(taskId).getOrganizationId(), e); } - }, executorService); - creations[taskId] = sharedRoleCreation; + }, executorService).exceptionally(throwable -> { + LOG.error(String.format( + "Exception occurred during creating a shared role: %s in organization: %s", + mainRoleName, sharedApplications.get(taskId).getOrganizationId()), throwable); + return null; + }); } - CompletableFuture allOfCreations = CompletableFuture.allOf(creations); - allOfCreations.join(); break; case RoleConstants.ORGANIZATION: /* - TODO: Need to create organization roles in suborgs only if the role is - attahced to at least on shared role - on new org role creation, this role can't be associated to an app. - therefore this logic can be removed - + Organization audience roles can't be attached to an application at the same time of role creation. + Organization audience role get shared to other application only if that role is associated with any + shared application in the organization. So nothing do in this case. */ - // If the audienced organization is a shared organization, create the role in the shared orgs. - List childOrganizations = - getOrganizationManager().getChildOrganizations(roleOrgId, true); - for (BasicOrganization childOrg : childOrganizations) { - String sharedOrganizationId = childOrg.getId(); - String shareAppTenantDomain = - getOrganizationManager().resolveTenantDomain(sharedOrganizationId); - RoleBasicInfo sharedRoleInfo = - getRoleManagementServiceV2().addRole(mainRoleName, Collections.emptyList(), - Collections.emptyList(), Collections.emptyList(), - RoleConstants.ORGANIZATION, sharedOrganizationId, - shareAppTenantDomain); - // Add relationship between main role and shared role. - getRoleManagementServiceV2().addMainRoleToSharedRoleRelationship(mainRoleUUID, - sharedRoleInfo.getId(), roleTenantDomain, shareAppTenantDomain); - } break; default: LOG.error("Unsupported audience type: " + roleAudienceType); } } catch (OrganizationManagementException e) { throw new IdentityEventException("Error occurred while retrieving shared applications.", e); - } catch (IdentityRoleManagementException e) { - throw new IdentityEventException("Error occurred while adding main role to shared role relationship.", e); - } - } - - private void createOrganizationRolesOnAppSharing(Map eventProperties) - throws IdentityEventException { - - String parentOrganizationId = - (String) eventProperties.get(OrgApplicationMgtConstants.EVENT_PROP_PARENT_ORGANIZATION_ID); - String parentApplicationId = - (String) eventProperties.get(OrgApplicationMgtConstants.EVENT_PROP_PARENT_APPLICATION_ID); - String sharedOrganizationId = - (String) eventProperties.get(OrgApplicationMgtConstants.EVENT_PROP_SHARED_ORGANIZATION_ID); - String sharedApplicationId = - (String) eventProperties.get(OrgApplicationMgtConstants.EVENT_PROP_SHARED_APPLICATION_ID); - try { - String mainApplicationTenantDomain = getOrganizationManager().resolveTenantDomain(parentOrganizationId); - String allowedAudienceForRoleAssociation = - getApplicationMgtService().getAllowedAudienceForRoleAssociation(parentApplicationId, - mainApplicationTenantDomain); - boolean hasAppAudiencedRoles = - RoleConstants.APPLICATION.equalsIgnoreCase(allowedAudienceForRoleAssociation); - if (!hasAppAudiencedRoles) { - // TODO: handle organization audience role creation if they doesn't exist in sub org. - return; - } - // Create the role if not exists, and add the relationship. - String sharedApplicationTenantDomain = getOrganizationManager().resolveTenantDomain(sharedOrganizationId); - // Get parent organization's roles which has application audience. - String filter = RoleConstants.AUDIENCE_ID + " " + RoleConstants.EQ + " " + parentApplicationId; - List parentOrgRoles = - getRoleManagementServiceV2().getRoles(filter, null, 0, null, null, - mainApplicationTenantDomain); - for (RoleBasicInfo parentOrgRole : parentOrgRoles) { - String parentOrgRoleName = parentOrgRole.getName(); - // Create the role in the sub org. - RoleBasicInfo subOrgRole = - getRoleManagementServiceV2().addRole(parentOrgRoleName, Collections.emptyList(), - Collections.emptyList(), Collections.emptyList(), RoleConstants.APPLICATION, - sharedApplicationId, sharedApplicationTenantDomain); - // Add relationship between parent org role and sub org role. - getRoleManagementServiceV2().addMainRoleToSharedRoleRelationship(parentOrgRole.getId(), - subOrgRole.getId(), mainApplicationTenantDomain, sharedApplicationTenantDomain); - } - } catch (IdentityApplicationManagementException e) { - throw new IdentityEventException("Error occurred checking main application allowed role audience.", e); - } catch (OrganizationManagementException e) { - throw new IdentityEventException("Error occurred while resolving tenant domain from organization id.", e); - } catch (IdentityRoleManagementException e) { - throw new IdentityEventException("Error occurred while adding main role to shared role relationship.", e); } } @@ -264,9 +146,4 @@ private static OrgApplicationManager getOrgApplicationManager() { return OrganizationManagementHandlerDataHolder.getInstance().getOrgApplicationManager(); } - - private static ApplicationManagementService getApplicationMgtService() { - - return OrganizationManagementHandlerDataHolder.getInstance().getApplicationManagementService(); - } } diff --git a/components/org.wso2.carbon.identity.organization.management.handler/src/main/java/org/wso2/carbon/identity/organization/management/handler/listener/SharedRoleMgtListener.java b/components/org.wso2.carbon.identity.organization.management.handler/src/main/java/org/wso2/carbon/identity/organization/management/handler/listener/SharedRoleMgtListener.java index 5b4e6a808..c47191d8a 100644 --- a/components/org.wso2.carbon.identity.organization.management.handler/src/main/java/org/wso2/carbon/identity/organization/management/handler/listener/SharedRoleMgtListener.java +++ b/components/org.wso2.carbon.identity.organization.management.handler/src/main/java/org/wso2/carbon/identity/organization/management/handler/listener/SharedRoleMgtListener.java @@ -18,6 +18,8 @@ package org.wso2.carbon.identity.organization.management.handler.listener; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException; @@ -26,7 +28,9 @@ import org.wso2.carbon.identity.application.mgt.ApplicationManagementService; import org.wso2.carbon.identity.application.mgt.listener.AbstractApplicationMgtListener; import org.wso2.carbon.identity.core.util.IdentityTenantUtil; +import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.organization.management.application.OrgApplicationManager; +import org.wso2.carbon.identity.organization.management.application.model.SharedApplication; import org.wso2.carbon.identity.organization.management.handler.internal.OrganizationManagementHandlerDataHolder; import org.wso2.carbon.identity.organization.management.service.OrganizationManager; import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException; @@ -34,13 +38,20 @@ import org.wso2.carbon.identity.role.v2.mgt.core.AssociatedApplication; import org.wso2.carbon.identity.role.v2.mgt.core.IdentityRoleManagementException; import org.wso2.carbon.identity.role.v2.mgt.core.Role; +import org.wso2.carbon.identity.role.v2.mgt.core.RoleBasicInfo; import org.wso2.carbon.identity.role.v2.mgt.core.RoleConstants; import org.wso2.carbon.identity.role.v2.mgt.core.RoleManagementService; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.stream.Collectors; +import static org.wso2.carbon.identity.organization.management.application.constant.OrgApplicationMgtConstants.IS_FRAGMENT_APP; import static org.wso2.carbon.identity.organization.management.service.constant.OrganizationManagementConstants.SUPER_ORG_ID; /** @@ -49,6 +60,12 @@ public class SharedRoleMgtListener extends AbstractApplicationMgtListener { private static final Log LOG = LogFactory.getLog(SharedRoleMgtListener.class); + private static final String REMOVED_APPLICATION_AUDIENCE_ROLES = "removedApplicationAudienceRoles"; + private static final String ADDED_APPLICATION_AUDIENCE_ROLES = "addedApplicationAudienceRoles"; + private static final String REMOVED_ORGANIZATION_AUDIENCE_ROLES = "removedOrganizationAudienceRoles"; + private static final String ADDED_ORGANIZATION_AUDIENCE_ROLES = "addedOrganizationAudienceRoles"; + + private final ExecutorService executorService = Executors.newFixedThreadPool(5); @Override public int getDefaultOrderId() { @@ -56,6 +73,385 @@ public int getDefaultOrderId() { return 49; } + @Override + public boolean doPostCreateApplication(ServiceProvider serviceProvider, String tenantDomain, String userName) + throws IdentityApplicationManagementException { + + // If the creating application is an application of tenant(i.e primary org) nothing to do here. + try { + if (!OrganizationManagementUtil.isOrganization(tenantDomain)) { + return true; + } + boolean isSharedAppInOrg = Arrays.stream(serviceProvider.getSpProperties()) + .anyMatch(p -> IS_FRAGMENT_APP.equalsIgnoreCase(p.getName()) && Boolean.parseBoolean(p.getValue())); + if (!isSharedAppInOrg) { + // This is not a valid case. Organization could only have shared applications. + return true; + } + ServiceProvider sharedApplication = + getApplicationByName(serviceProvider.getApplicationName(), tenantDomain); + if (sharedApplication == null) { + return false; + } + String sharedAppId = sharedApplication.getApplicationResourceId(); + String sharedAppOrgId = getOrganizationManager().resolveOrganizationId(tenantDomain); + String sharedAppTenantDomain = getOrganizationManager().resolveTenantDomain(sharedAppOrgId); + // Resolve the main application details. + String mainAppId = + getOrgApplicationManager().getMainApplicationIdForGivenSharedApp(sharedAppId, sharedAppOrgId); + if (mainAppId == null) { + return false; + } + int mainAppTenantId = getApplicationMgtService().getTenantIdByApp(mainAppId); + String mainAppTenantDomain = IdentityTenantUtil.getTenantDomain(mainAppTenantId); + String allowedAudienceForRoleAssociationInMainApp = + getApplicationMgtService().getAllowedAudienceForRoleAssociation(mainAppId, mainAppTenantDomain); + + switch (allowedAudienceForRoleAssociationInMainApp) { + case RoleConstants.APPLICATION: + // Create the roles, and add the relationship. + createSharedRolesWithAppAudience(mainAppId, mainAppTenantDomain, sharedAppId, + sharedAppTenantDomain); + break; + default: + // Create the role if not exists, and add the relationship. + List associatedRolesOfApplication = + getApplicationMgtService().getAssociatedRolesOfApplication( + mainAppId, mainAppTenantDomain); + createSharedRolesWithOrgAudience(associatedRolesOfApplication, mainAppTenantDomain, sharedAppOrgId); + break; + } + } catch (OrganizationManagementException | IdentityRoleManagementException e) { + throw new IdentityApplicationManagementException( + String.format("Error while sharing roles related to application %s.", + serviceProvider.getApplicationID()), e); + } + return super.doPostCreateApplication(serviceProvider, tenantDomain, userName); + } + + private void createSharedRolesWithOrgAudience(List rolesList, String mainAppTenantDomain, + String sharedAppOrgId) + throws IdentityRoleManagementException, OrganizationManagementException { + + if (rolesList == null) { + return; + } + String sharedAppTenantDomain = getOrganizationManager().resolveTenantDomain(sharedAppOrgId); + for (RoleV2 role : rolesList) { + // Check if the role exists in the application shared org. + boolean roleExistsInSharedOrg = + getRoleManagementServiceV2().isExistingRoleName(role.getName(), RoleConstants.ORGANIZATION, + sharedAppOrgId, sharedAppTenantDomain); + Map mainRoleToSharedRoleMappingInSharedOrg = + getRoleManagementServiceV2().getMainRoleToSharedRoleMappingsBySubOrg( + Collections.singletonList(role.getId()), sharedAppTenantDomain); + boolean roleRelationshipExistsInSharedOrg = + MapUtils.isNotEmpty(mainRoleToSharedRoleMappingInSharedOrg); + if (roleExistsInSharedOrg && !roleRelationshipExistsInSharedOrg) { + // Add relationship between main role and shared role. + String roleIdInSharedOrg = + getRoleManagementServiceV2().getRoleIdByName(role.getName(), RoleConstants.ORGANIZATION, + sharedAppOrgId, sharedAppTenantDomain); + getRoleManagementServiceV2().addMainRoleToSharedRoleRelationship(role.getId(), + roleIdInSharedOrg, mainAppTenantDomain, sharedAppTenantDomain); + } else if (!roleExistsInSharedOrg && !roleRelationshipExistsInSharedOrg) { + // Create the role in the shared org. + RoleBasicInfo sharedRole = + getRoleManagementServiceV2().addRole(role.getName(), Collections.emptyList(), + Collections.emptyList(), Collections.emptyList(), RoleConstants.ORGANIZATION, + sharedAppOrgId, sharedAppTenantDomain); + // Add relationship between main role and shared role. + getRoleManagementServiceV2().addMainRoleToSharedRoleRelationship(role.getId(), + sharedRole.getId(), mainAppTenantDomain, sharedAppTenantDomain); + } + } + } + + private void createSharedRolesWithAppAudience(String mainAppId, String mainAppTenantDomain, String sharedAppId, + String sharedAppTenantDomain) throws IdentityRoleManagementException { + + // Get parent organization's roles which has application audience. + String filter = RoleConstants.AUDIENCE_ID + " " + RoleConstants.EQ + " " + mainAppId; + List parentOrgRoles = + getRoleManagementServiceV2().getRoles(filter, null, 0, null, null, mainAppTenantDomain); + for (RoleBasicInfo parentOrgRole : parentOrgRoles) { + String parentOrgRoleName = parentOrgRole.getName(); + // Create the role in the shared org. + RoleBasicInfo subOrgRole = getRoleManagementServiceV2().addRole(parentOrgRoleName, Collections.emptyList(), + Collections.emptyList(), Collections.emptyList(), RoleConstants.APPLICATION, sharedAppId, + sharedAppTenantDomain); + // Add relationship between main role and the shared role. + getRoleManagementServiceV2().addMainRoleToSharedRoleRelationship(parentOrgRole.getId(), subOrgRole.getId(), + mainAppTenantDomain, sharedAppTenantDomain); + } + } + + @Override + public boolean doPreUpdateApplication(ServiceProvider serviceProvider, String tenantDomain, String userName) + throws IdentityApplicationManagementException { + + // Associated role changes on main applications in tenant need to be handled here. + try { + if (OrganizationManagementUtil.isOrganization(tenantDomain)) { + return true; + } + String applicationResourceId = serviceProvider.getApplicationResourceId(); + // Get the currently associated roles set from DB/cache. + String existingAllowedAudienceForRoleAssociation = + getApplicationMgtService().getAllowedAudienceForRoleAssociation(applicationResourceId, + tenantDomain); + List existingAssociatedRolesList = + getApplicationMgtService().getAssociatedRolesOfApplication(applicationResourceId, tenantDomain); + + String updatedAllowedAudienceForRoleAssociation = + serviceProvider.getAssociatedRolesConfig() == null ? RoleConstants.ORGANIZATION : + serviceProvider.getAssociatedRolesConfig().getAllowedAudience(); + List updatedAssociatedRolesList = + serviceProvider.getAssociatedRolesConfig() == null ? Collections.emptyList() : + serviceProvider.getAssociatedRolesConfig().getRoles(); + + if (CollectionUtils.isEmpty(existingAssociatedRolesList) && + CollectionUtils.isEmpty(updatedAssociatedRolesList)) { + // No change in roles list. + return true; + } + + /* + if old and new audiences are equals, need to handle the role diff. + */ + if (existingAllowedAudienceForRoleAssociation.equalsIgnoreCase(updatedAllowedAudienceForRoleAssociation)) { + switch (updatedAllowedAudienceForRoleAssociation) { + case RoleConstants.APPLICATION: + List addedApplicationAudienceRoles = updatedAssociatedRolesList.stream() + .filter(updatedRole -> !existingAssociatedRolesList.contains(updatedRole)) + .collect(Collectors.toList()); + + List removedApplicationAudienceRoles = existingAssociatedRolesList.stream() + .filter(existingRole -> !updatedAssociatedRolesList.contains(existingRole)) + .collect(Collectors.toList()); + // Add to threadLocal. + IdentityUtil.threadLocalProperties.get() + .put(ADDED_APPLICATION_AUDIENCE_ROLES, addedApplicationAudienceRoles); + IdentityUtil.threadLocalProperties.get() + .put(REMOVED_APPLICATION_AUDIENCE_ROLES, removedApplicationAudienceRoles); + return true; + default: + if (existingAssociatedRolesList.equals(updatedAssociatedRolesList)) { + // Nothing to change in shared applications' organizations. + return true; + } + // Get the added roles and removed roles. + List addedOrganizationAudienceRoles = updatedAssociatedRolesList.stream() + .filter(updatedRole -> !existingAssociatedRolesList.contains(updatedRole)) + .collect(Collectors.toList()); + + List removedOrganizationAudienceRoles = existingAssociatedRolesList.stream() + .filter(existingRole -> !updatedAssociatedRolesList.contains(existingRole)) + .collect(Collectors.toList()); + // Add to threadLocal. + IdentityUtil.threadLocalProperties.get() + .put(ADDED_ORGANIZATION_AUDIENCE_ROLES, addedOrganizationAudienceRoles); + IdentityUtil.threadLocalProperties.get() + .put(REMOVED_ORGANIZATION_AUDIENCE_ROLES, removedOrganizationAudienceRoles); + return true; + } + } + + /* + If audience has changed from application to organization, all previous associated roles will be deleted. + For updated organization roles, create shared role in organizations below if applicable. + */ + if (RoleConstants.APPLICATION.equalsIgnoreCase(existingAllowedAudienceForRoleAssociation) && + RoleConstants.ORGANIZATION.equalsIgnoreCase(updatedAllowedAudienceForRoleAssociation)) { + + // Add to thread local. + IdentityUtil.threadLocalProperties.get() + .put(REMOVED_APPLICATION_AUDIENCE_ROLES, existingAssociatedRolesList); + IdentityUtil.threadLocalProperties.get() + .put(ADDED_ORGANIZATION_AUDIENCE_ROLES, updatedAssociatedRolesList); + return true; + } + + /* + If audience has changed from organization to application, need to remove the organization roles. + Nothing to handle in application audience roles because they will be added/deleted in shared orgs + based on role creation/deletion. + */ + if (RoleConstants.ORGANIZATION.equalsIgnoreCase(existingAllowedAudienceForRoleAssociation) && + RoleConstants.APPLICATION.equalsIgnoreCase(updatedAllowedAudienceForRoleAssociation)) { + + // Add to thread local. + IdentityUtil.threadLocalProperties.get() + .put(REMOVED_ORGANIZATION_AUDIENCE_ROLES, existingAssociatedRolesList); + return true; + } + } catch (OrganizationManagementException e) { + throw new IdentityApplicationManagementException( + String.format("Error while checking shared roles to be updated related to application %s update.", + serviceProvider.getApplicationID()), e); + } + return true; + } + + @Override + public boolean doPostUpdateApplication(ServiceProvider serviceProvider, String tenantDomain, String userName) + throws IdentityApplicationManagementException { + + try { + if (OrganizationManagementUtil.isOrganization(tenantDomain)) { + return true; + } + Object addedAppRoles = IdentityUtil.threadLocalProperties.get().get(ADDED_APPLICATION_AUDIENCE_ROLES); + if (addedAppRoles != null) { + List addedAppRolesList = (List) addedAppRoles; + handleAddedApplicationAudienceRolesOnAppUpdate(addedAppRolesList, serviceProvider, tenantDomain); + } + + Object removedAppRoles = IdentityUtil.threadLocalProperties.get().get(REMOVED_APPLICATION_AUDIENCE_ROLES); + if (removedAppRoles != null) { + List removedAppRolesList = (List) removedAppRoles; + handleRemovedApplicationAudienceRolesOnAppUpdate(removedAppRolesList, tenantDomain); + } + + Object addedOrgRoles = IdentityUtil.threadLocalProperties.get().get(ADDED_ORGANIZATION_AUDIENCE_ROLES); + if (addedOrgRoles != null) { + List addedOrgRolesList = (List) addedOrgRoles; + handleAddedOrganizationAudienceRolesOnAppUpdate(addedOrgRolesList, serviceProvider, tenantDomain); + + } + + Object removedOrgRoles = IdentityUtil.threadLocalProperties.get().get(REMOVED_ORGANIZATION_AUDIENCE_ROLES); + if (removedOrgRoles != null) { + List removedOrgRolesList = (List) removedOrgRoles; + handleRemovedOrganizationAudienceRolesOnAppUpdate(removedOrgRolesList, serviceProvider, tenantDomain); + } + } catch (OrganizationManagementException | IdentityRoleManagementException e) { + throw new IdentityApplicationManagementException( + String.format("Error while updating shared roles related to application %s update.", + serviceProvider.getApplicationID()), e); + } finally { + IdentityUtil.threadLocalProperties.get().remove(ADDED_APPLICATION_AUDIENCE_ROLES); + IdentityUtil.threadLocalProperties.get().remove(REMOVED_APPLICATION_AUDIENCE_ROLES); + IdentityUtil.threadLocalProperties.get().remove(ADDED_ORGANIZATION_AUDIENCE_ROLES); + IdentityUtil.threadLocalProperties.get().remove(REMOVED_ORGANIZATION_AUDIENCE_ROLES); + } + return true; + } + + private void handleRemovedOrganizationAudienceRolesOnAppUpdate(List removedOrgRolesList, + ServiceProvider serviceProvider, String tenantDomain) + throws OrganizationManagementException, IdentityRoleManagementException { + + if (CollectionUtils.isEmpty(removedOrgRolesList)) { + return; + } + String mainAppId = serviceProvider.getApplicationResourceId(); + String mainAppOrgId = getOrganizationManager().resolveOrganizationId(tenantDomain); + List sharedApplications = + getOrgApplicationManager().getSharedApplications(mainAppOrgId, mainAppId); + if (CollectionUtils.isEmpty(sharedApplications)) { + return; + } + for (SharedApplication sharedApplication : sharedApplications) { + CompletableFuture.runAsync(() -> { + String sharedAppOrgId = sharedApplication.getOrganizationId(); + try { + handleOrganizationAudiencedSharedRoleDeletion(removedOrgRolesList, + serviceProvider.getApplicationResourceId(), + tenantDomain, sharedAppOrgId); + } catch (IdentityRoleManagementException | OrganizationManagementException e) { + LOG.error(String.format("Exception occurred during deleting roles from organization %s", + sharedApplication.getOrganizationId()), e); + } + }, executorService).exceptionally(throwable -> { + LOG.error(String.format("Exception occurred during deleting roles from organization %s", + sharedApplication.getOrganizationId()), throwable); + return null; + }); + } + } + + private void handleAddedOrganizationAudienceRolesOnAppUpdate(List addedOrgRolesList, + ServiceProvider serviceProvider, String tenantDomain) + throws OrganizationManagementException, IdentityRoleManagementException { + + if (CollectionUtils.isEmpty(addedOrgRolesList)) { + return; + } + String mainAppId = serviceProvider.getApplicationResourceId(); + String mainAppOrgId = getOrganizationManager().resolveOrganizationId(tenantDomain); + List sharedApplications = + getOrgApplicationManager().getSharedApplications(mainAppOrgId, mainAppId); + if (CollectionUtils.isEmpty(sharedApplications)) { + return; + } + + for (SharedApplication sharedApplication : sharedApplications) { + CompletableFuture.runAsync(() -> { + String sharedAppOrgId = sharedApplication.getOrganizationId(); + try { + createSharedRolesWithOrgAudience(addedOrgRolesList, tenantDomain, sharedAppOrgId); + } catch (IdentityRoleManagementException | OrganizationManagementException e) { + LOG.error(String.format("Exception occurred while adding shared roles to organization: %s", + sharedApplication.getOrganizationId()), e); + } + }, executorService).exceptionally(throwable -> { + LOG.error(String.format("Exception occurred while adding shared roles to organization: %s", + sharedApplication.getOrganizationId()), throwable); + return null; + }); + } + } + + private void handleRemovedApplicationAudienceRolesOnAppUpdate(List removedAppRolesList, String tenantDomain) + throws IdentityRoleManagementException { + + if (CollectionUtils.isEmpty(removedAppRolesList)) { + return; + } + /* + Delete the application audience roles from parent organization. Deleting their shared roles also handled inside. + */ + for (RoleV2 removedRole : removedAppRolesList) { + getRoleManagementServiceV2().deleteRole(removedRole.getId(), tenantDomain); + } + } + + private void handleAddedApplicationAudienceRolesOnAppUpdate(List addedAppRolesList, + ServiceProvider serviceProvider, String tenantDomain) + throws OrganizationManagementException, IdentityRoleManagementException { + + if (CollectionUtils.isEmpty(addedAppRolesList)) { + return; + } + // Get shared applications of the given main app, and share the role. + String mainAppId = serviceProvider.getApplicationResourceId(); + String mainAppOrgId = getOrganizationManager().resolveOrganizationId(tenantDomain); + + List sharedApplications = + getOrgApplicationManager().getSharedApplications(mainAppOrgId, mainAppId); + if (CollectionUtils.isEmpty(sharedApplications)) { + return; + } + + for (RoleV2 parentRole : addedAppRolesList) { + for (SharedApplication sharedApplication : sharedApplications) { + String sharedAppOrgId = sharedApplication.getOrganizationId(); + String sharedAppTenantDomain = getOrganizationManager().resolveTenantDomain(sharedAppOrgId); + String parentAppRoleName = parentRole.getName(); + // Create the role in the shared org. + RoleBasicInfo subOrgRole = + getRoleManagementServiceV2().addRole(parentAppRoleName, Collections.emptyList(), + Collections.emptyList(), Collections.emptyList(), RoleConstants.APPLICATION, + sharedApplication.getSharedApplicationId(), + sharedAppTenantDomain); + // Add relationship between main role and the shared role. + getRoleManagementServiceV2().addMainRoleToSharedRoleRelationship(parentRole.getId(), subOrgRole.getId(), + tenantDomain, sharedAppTenantDomain); + } + } + } + @Override public boolean doPreDeleteApplication(String applicationName, String tenantDomain, String userName) throws IdentityApplicationManagementException { @@ -71,7 +467,6 @@ public boolean doPreDeleteApplication(String applicationName, String tenantDomai return false; } String sharedAppId = sharedApplication.getApplicationResourceId(); - // Get all the shared applications of the deleting app. String sharedAppOrgId = getOrganizationManager().resolveOrganizationId(tenantDomain); // Resolve the main application details. String mainAppId = @@ -81,10 +476,7 @@ public boolean doPreDeleteApplication(String applicationName, String tenantDomai } int mainAppTenantId = getApplicationMgtService().getTenantIdByApp(mainAppId); String mainAppTenantDomain = IdentityTenantUtil.getTenantDomain(mainAppTenantId); - String mainAppOrgId = getOrganizationManager().resolveOrganizationId(mainAppTenantDomain); - if (mainAppOrgId == null) { - mainAppOrgId = SUPER_ORG_ID; - } + String allowedAudienceForRoleAssociationInMainApp = getApplicationMgtService().getAllowedAudienceForRoleAssociation(mainAppId, mainAppTenantDomain); boolean hasAppAudiencedRoles = @@ -95,8 +487,10 @@ public boolean doPreDeleteApplication(String applicationName, String tenantDomai } // Handing organization audienced roles associated case. - handleOrganizationAudiencedSharedRoleDeletion(mainAppId, mainAppTenantDomain, mainAppOrgId, tenantDomain, - sharedAppOrgId); + List associatedRolesOfMainApplication = getApplicationMgtService() + .getAssociatedRolesOfApplication(mainAppId, mainAppTenantDomain); + handleOrganizationAudiencedSharedRoleDeletion(associatedRolesOfMainApplication, mainAppId, + mainAppTenantDomain, sharedAppOrgId); } catch (OrganizationManagementException | IdentityRoleManagementException e) { throw new IdentityApplicationManagementException( "Error while deleting organization roles associated to the app.", e); @@ -104,18 +498,18 @@ public boolean doPreDeleteApplication(String applicationName, String tenantDomai return super.doPreDeleteApplication(applicationName, tenantDomain, userName); } - private void handleOrganizationAudiencedSharedRoleDeletion(String mainApplicationId, + private void handleOrganizationAudiencedSharedRoleDeletion(List rolesList, String mainApplicationId, String mainApplicationTenantDomain, - String mainApplicationOrgId, - String unsharedApplicationTenantDomain, - String appUnsharedOrgId) - throws IdentityRoleManagementException, IdentityApplicationManagementException, - OrganizationManagementException { - - List associatedRolesOfMainApplication = getApplicationMgtService() - .getAssociatedRolesOfApplication(mainApplicationId, mainApplicationTenantDomain); + String sharedAppOrgId) + throws IdentityRoleManagementException, OrganizationManagementException { + + String mainApplicationOrgId = getOrganizationManager().resolveOrganizationId(mainApplicationTenantDomain); + if (mainApplicationOrgId == null) { + mainApplicationOrgId = SUPER_ORG_ID; + } + String unsharedApplicationTenantDomain = getOrganizationManager().resolveTenantDomain(sharedAppOrgId); List mainAppRoleIds = - associatedRolesOfMainApplication.stream().map(RoleV2::getId).collect(Collectors.toList()); + rolesList.stream().map(RoleV2::getId).collect(Collectors.toList()); Map mainRoleToSharedRoleMappingsInSubOrg = getRoleManagementServiceV2() .getMainRoleToSharedRoleMappingsBySubOrg(mainAppRoleIds, unsharedApplicationTenantDomain); @@ -130,10 +524,10 @@ private void handleOrganizationAudiencedSharedRoleDeletion(String mainApplicatio } /* If the only associated application is the main app in this flow, delete the role in - the app unsharing org. + the org. */ if (associatedApplications.size() == 1 && mainApplicationId.equals(associatedApplications.get(0).getId())) { - // Delete the role in app unsharing org. + // Delete the role in org. getRoleManagementServiceV2().deleteRole(mainRoleToSharedRoleMappingsInSubOrg.get(mainAppRoleId), unsharedApplicationTenantDomain); break; @@ -145,14 +539,14 @@ private void handleOrganizationAudiencedSharedRoleDeletion(String mainApplicatio } boolean applicationSharedWithGivenOrganization = getOrgApplicationManager().isApplicationSharedWithGivenOrganization( - associatedApplication.getId(), mainApplicationOrgId, appUnsharedOrgId); + associatedApplication.getId(), mainApplicationOrgId, sharedAppOrgId); if (applicationSharedWithGivenOrganization) { isRoleUsedByAnotherSharedApp = true; break; } } if (!isRoleUsedByAnotherSharedApp) { - // Delete the role in app unsharing org. + // Delete the role in org. getRoleManagementServiceV2().deleteRole(mainRoleToSharedRoleMappingsInSubOrg.get(mainAppRoleId), unsharedApplicationTenantDomain); break;