From c5cb4aad84779404c1ba9fdb037e1a1108c8e7c6 Mon Sep 17 00:00:00 2001 From: Shan Chathusanda Jayathilaka Date: Tue, 12 Nov 2024 15:08:18 +0530 Subject: [PATCH] Resolve role sharing conflicts in app sharing --- .../pom.xml | 4 + .../handler/SharedRoleMgtHandler.java | 107 ++++++++++++++++-- .../listener/SharedRoleMgtListener.java | 8 +- pom.xml | 5 + 4 files changed, 114 insertions(+), 10 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 966bfaea7..ffeda1418 100644 --- a/components/org.wso2.carbon.identity.organization.management.handler/pom.xml +++ b/components/org.wso2.carbon.identity.organization.management.handler/pom.xml @@ -80,6 +80,10 @@ org.wso2.carbon.identity.framework org.wso2.carbon.identity.application.mgt + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.central.log.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 5bd4a6a9a..bc09662f8 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 @@ -26,6 +26,9 @@ import org.wso2.carbon.identity.application.common.model.ApplicationBasicInfo; import org.wso2.carbon.identity.application.common.model.RoleV2; import org.wso2.carbon.identity.application.mgt.ApplicationManagementService; +import org.wso2.carbon.identity.central.log.mgt.utils.LogConstants; +import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils; +import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.event.IdentityEventConstants; import org.wso2.carbon.identity.event.IdentityEventException; import org.wso2.carbon.identity.event.event.Event; @@ -42,9 +45,11 @@ import org.wso2.carbon.identity.role.v2.mgt.core.RoleManagementService; 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.utils.AuditLog; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -72,6 +77,9 @@ public void handleEvent(Event event) throws IdentityEventException { case IdentityEventConstants.Event.POST_ADD_ROLE_V2_EVENT: createSharedRolesOnNewRoleCreation(eventProperties); break; + case OrgApplicationMgtConstants.EVENT_PRE_SHARE_APPLICATION: + checkSharingRoleConflicts(eventProperties); + break; default: if (LOG.isDebugEnabled()) { LOG.debug("Unsupported event: " + eventName); @@ -271,13 +279,30 @@ private void createSharedRolesOnNewRoleCreation(Map eventPropert for (BasicOrganization organization : applicationSharedOrganizations) { String shareAppTenantDomain = getOrganizationManager().resolveTenantDomain(organization.getId()); - RoleBasicInfo sharedRoleInfo = getRoleManagementServiceV2().addRole(mainRoleName, - Collections.emptyList(), - Collections.emptyList(), - Collections.emptyList(), RoleConstants.ORGANIZATION, organization.getId(), - shareAppTenantDomain); - getRoleManagementServiceV2().addMainRoleToSharedRoleRelationship(mainRoleUUID, - sharedRoleInfo.getId(), roleTenantDomain, shareAppTenantDomain); + if (!getRoleManagementServiceV2().isExistingRoleName(mainRoleName, RoleConstants.ORGANIZATION, + organization.getId(), shareAppTenantDomain)) { + RoleBasicInfo sharedRoleInfo = getRoleManagementServiceV2().addRole(mainRoleName, + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), RoleConstants.ORGANIZATION, organization.getId(), + shareAppTenantDomain); + getRoleManagementServiceV2().addMainRoleToSharedRoleRelationship(mainRoleUUID, + sharedRoleInfo.getId(), roleTenantDomain, shareAppTenantDomain); + } else { + if (LoggerUtils.isEnableV2AuditLogs()) { + String username = PrivilegedCarbonContext.getThreadLocalCarbonContext() + .getUsername(); + String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext() + .getTenantDomain(); + AuditLog.AuditLogBuilder auditLogBuilder = new AuditLog.AuditLogBuilder( + IdentityUtil.getInitiatorId(username, tenantDomain), + LoggerUtils.Target.User.name(), mainRoleName, LoggerUtils.Target.Role.name(), + LogConstants.UserManagement.ADD_ROLE_ACTION) + .data(buildAuditData(roleOrgId, null, organization.getId(), mainRoleName, + mainRoleUUID, "Role conflict")); + LoggerUtils.triggerAuditLogEvent(auditLogBuilder, true); + } + } } break; default: @@ -289,6 +314,60 @@ private void createSharedRolesOnNewRoleCreation(Map eventPropert } } + private void checkSharingRoleConflicts(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 sharedAppTenantDomain = getOrganizationManager().resolveTenantDomain(sharedOrganizationId); + String mainAppTenantDomain = getOrganizationManager().resolveTenantDomain(parentOrganizationId); + String allowedAudienceForRoleAssociationInMainApp = getApplicationMgtService(). + getAllowedAudienceForRoleAssociation(parentApplicationId, mainAppTenantDomain); + if (RoleConstants.ORGANIZATION.equals(allowedAudienceForRoleAssociationInMainApp.toLowerCase())) { + List associatedRolesOfApplication = getApplicationMgtService(). + getAssociatedRolesOfApplication(parentApplicationId, mainAppTenantDomain); + for (RoleV2 roleV2 : associatedRolesOfApplication) { + boolean roleExistsInSharedOrg = getRoleManagementServiceV2().isExistingRoleName(roleV2.getName(), + RoleConstants.ORGANIZATION, sharedOrganizationId, sharedAppTenantDomain); + Map mainRoleToSharedRoleMappingInSharedOrg = + getRoleManagementServiceV2().getMainRoleToSharedRoleMappingsBySubOrg( + Collections.singletonList(roleV2.getId()), sharedAppTenantDomain); + boolean roleRelationshipExistsInSharedOrg = + MapUtils.isNotEmpty(mainRoleToSharedRoleMappingInSharedOrg); + if (roleExistsInSharedOrg && !roleRelationshipExistsInSharedOrg) { + // If the role exists in the shared org, but the relationship does not exist then this role is + // created directly in the sub organization level. So this is a conflict to share the role + // with same name and organization audience to the sub organization. + if (LoggerUtils.isEnableV2AuditLogs()) { + String username = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername(); + String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext(). + getTenantDomain(); + AuditLog.AuditLogBuilder auditLogBuilder = new AuditLog.AuditLogBuilder( + IdentityUtil.getInitiatorId(username, tenantDomain), + LoggerUtils.Target.User.name(), roleV2.getName(), LoggerUtils.Target.Role.name(), + LogConstants.ApplicationManagement.CREATE_APPLICATION_ACTION). + data(buildAuditData(parentOrganizationId, parentApplicationId, + sharedOrganizationId, roleV2.getName(), roleV2.getId(), "Role conflict")); + LoggerUtils.triggerAuditLogEvent(auditLogBuilder, true); + } + throw new IdentityEventException(String.format("Organization %s has a non shared role with " + + "name %s, ", sharedOrganizationId, roleV2.getName())); + } + } + } + } catch (OrganizationManagementException | IdentityRoleManagementException | + IdentityApplicationManagementException e) { + throw new IdentityEventException(String.format("Error while sharing roles related to application %s.", + sharedApplicationId), e); + } + } + private static RoleManagementService getRoleManagementServiceV2() { return OrganizationManagementHandlerDataHolder.getInstance().getRoleManagementServiceV2(); @@ -308,4 +387,18 @@ private static ApplicationManagementService getApplicationMgtService() { return OrganizationManagementHandlerDataHolder.getInstance().getApplicationManagementService(); } + + private Map buildAuditData(String parentOrganizationId, String parentApplicationId, + String sharedOrganizationId, String roleName, String roleId, + String failureReason) { + + Map auditData = new HashMap<>(); + auditData.put("parentOrganizationId", parentOrganizationId); + auditData.put("parentApplicationId", parentApplicationId); + auditData.put("sharedOrganizationId", sharedOrganizationId); + auditData.put("roleId", roleId); + auditData.put("roleName", roleName); + auditData.put("failureReason", failureReason); + return auditData; + } } 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 5b852ef5d..6dd906a84 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 @@ -608,11 +608,13 @@ public boolean doPostGetAssociatedRolesOfApplication(List associatedRole throws IdentityApplicationManagementException { try { - if (!OrganizationManagementUtil.isOrganization(tenantDomain)) { + String mainAppId = applicationManagementService.getMainAppId(applicationUUID); + // If the main application id is null, then this is the main application. We can skip this operation based + // on that. + if (StringUtils.isEmpty(mainAppId)) { return true; } // Resolve the associated roles of shared application from main application details. - String mainAppId = applicationManagementService.getMainAppId(applicationUUID); int mainAppTenantId = applicationManagementService.getTenantIdByApp(mainAppId); String mainAppTenantDomain = IdentityTenantUtil.getTenantDomain(mainAppTenantId); List resolvedAssociatedRolesFromMainApp = @@ -641,7 +643,7 @@ public boolean doPostGetAssociatedRolesOfApplication(List associatedRole .collect(Collectors.toList()); associatedRolesOfApplication.clear(); associatedRolesOfApplication.addAll(associatedRolesOfSharedApplication); - } catch (OrganizationManagementException | IdentityRoleManagementException e) { + } catch (IdentityRoleManagementException e) { throw new IdentityApplicationManagementException(String.format( "Error while fetching the allowed audience for role association of application with: %s.", applicationUUID), e); diff --git a/pom.xml b/pom.xml index 6cc4a4b27..be0c1cb59 100644 --- a/pom.xml +++ b/pom.xml @@ -268,6 +268,11 @@ commons-collections ${apache.common.collection.version} + + org.wso2.carbon.identity.framework + org.wso2.carbon.identity.central.log.mgt + ${carbon.identity.framework.version} +