From 2d869e0c9f009f1c7eed1c715b77d7a786c0d381 Mon Sep 17 00:00:00 2001 From: VihangaMunasinghe Date: Fri, 3 Apr 2026 02:23:22 +0530 Subject: [PATCH 1/6] Fix the tenant and org differentiation logic --- .../notification/PushNotificationHandler.java | 8 +- .../notification/util/NotificationUtil.java | 42 ++++++- .../util/NotificationUtilTest.java | 115 +++++++++++++++++- 3 files changed, 157 insertions(+), 8 deletions(-) diff --git a/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/PushNotificationHandler.java b/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/PushNotificationHandler.java index 99950fb52..4df10bb6d 100644 --- a/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/PushNotificationHandler.java +++ b/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/PushNotificationHandler.java @@ -266,13 +266,9 @@ private PushNotificationData buildPushNotificationData(Event event) throws Ident placeholderValues.put(ORGANIZATION_NAME_PLACEHOLDER, organizationName); } - /* - * If the tenant domain is different from the organization name, then it is an organization user. Hence, - * the organization ID is the tenant domain. - */ String organizationId = null; - if (!tenantDomain.equals(organizationName)) { - organizationId = tenantDomain; + if (NotificationUtil.isOrganization(tenantDomain)) { + organizationId = NotificationUtil.getOrganizationUUID(tenantDomain); } else { // If tenant user, organizationName is null. organizationName = null; diff --git a/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtil.java b/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtil.java index ac41c6b0c..a194e0a8f 100644 --- a/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtil.java +++ b/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2024, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2016-2026, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. * * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except @@ -869,6 +869,46 @@ public static String resolveHumanReadableOrganizationName(String tenantDomain) t return organizationName; } + /** + * Check whether the given tenant domain belongs to an organization. + * + * @param tenetDomain Tenant domain. + * @return true if the tenant domain belongs to an organization, false otherwise. + * @throws IdentityEventException Error while checking whether the tenant domain belongs to an organization. + */ + public static boolean isOrganization(String tenetDomain) throws IdentityEventException { + + try { + return OrganizationManagementUtil.isOrganization(tenetDomain); + } catch (OrganizationManagementException e) { + throw new IdentityEventException(e.getMessage(), e); + } + } + + /** + * Get the organization UUID for the given tenant domain. + * + * @param tenantDomain Tenant domain. + * @return Organization UUID for the given tenant domain, or null if the tenant domain does not belong to an organization. + * @throws IdentityEventException Error while retrieving the organization UUID for the given tenant domain. + */ + public static String getOrganizationUUID(String tenantDomain) throws IdentityEventException { + + String organizationUUID = null; + try { + RealmService realmService = NotificationHandlerDataHolder.getInstance().getRealmService(); + int tenantId = IdentityTenantUtil.getTenantId(tenantDomain); + Tenant tenant = realmService.getTenantManager().getTenant(tenantId); + if (tenant == null) { + return null; + } + organizationUUID = tenant.getAssociatedOrganizationUUID(); + } catch (UserStoreException e) { + throw new IdentityEventException(e.getMessage(), e); + } + return organizationUUID; + } + /** * Get the notification locale. * diff --git a/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/test/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtilTest.java b/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/test/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtilTest.java index 4d62dab19..f6088dde2 100644 --- a/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/test/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtilTest.java +++ b/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/test/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtilTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2025, WSO2 LLC. (http://www.wso2.com). + * Copyright (c) 2022-2026, 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 @@ -43,13 +43,20 @@ import org.wso2.carbon.identity.core.URLBuilderException; import org.wso2.carbon.identity.core.internal.component.IdentityCoreServiceComponent; import org.wso2.carbon.identity.core.util.IdentityConfigParser; +import org.wso2.carbon.identity.core.util.IdentityTenantUtil; import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.event.IdentityEventConstants; import org.wso2.carbon.identity.event.event.Event; import org.wso2.carbon.identity.event.handler.notification.NotificationConstants; import org.wso2.carbon.identity.event.handler.notification.internal.NotificationHandlerDataHolder; +import org.wso2.carbon.identity.event.IdentityEventException; +import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException; import org.wso2.carbon.identity.organization.management.service.util.OrganizationManagementUtil; import org.wso2.carbon.identity.organization.management.service.util.Utils; +import org.wso2.carbon.user.api.Tenant; +import org.wso2.carbon.user.core.tenant.TenantManager; +import org.wso2.carbon.user.api.UserStoreException; +import org.wso2.carbon.user.core.service.RealmService; import org.wso2.carbon.utils.CarbonUtils; import org.wso2.carbon.utils.ConfigurationContextService; @@ -129,6 +136,9 @@ public class NotificationUtilTest { private static final String SAMPLE_ORGANIZATION_NAME = "OrganizationA"; private static final String SAMPLE_LOCALE = "fr-FR"; private static final String SAMPLE_EMAIL_BODY = "SampleEmailBody"; + private static final String SAMPLE_TENANT_DOMAIN = "sample.com"; + private static final String SAMPLE_ORG_UUID = "673b507c-6e29-40e1-8e87-0b24e9a97e12"; + private static final int SAMPLE_TENANT_ID = 5; private static final String ACCOUNT_RECOVERY_ENDPOINT_URL = "https://example.com/account/recovery"; private static final String AUTHENTICATION_ENDPOINT_URL = "https://example.com/authentication"; @@ -521,4 +531,107 @@ private void mockCarbonUtils(MockedStatic carbonUtils) { carbonUtils.when(() -> CarbonUtils.getServerConfiguration()).thenReturn(serverConfiguration); carbonUtils.when(CarbonUtils::getManagementTransport).thenReturn(DUMMY_PROTOCOL); } + + @DataProvider(name = "isOrganizationDataProvider") + public Object[][] isOrganizationDataProvider() { + + return new Object[][] { + {true}, + {false} + }; + } + + @Test(dataProvider = "isOrganizationDataProvider") + public void testIsOrganization(boolean expected) throws IdentityEventException { + + try (MockedStatic mockedOrgManagementUtil = + mockStatic(OrganizationManagementUtil.class)) { + + mockedOrgManagementUtil.when(() -> OrganizationManagementUtil.isOrganization(SAMPLE_TENANT_DOMAIN)) + .thenReturn(expected); + + boolean result = NotificationUtil.isOrganization(SAMPLE_TENANT_DOMAIN); + assertEquals(result, expected); + } + } + + @Test(expectedExceptions = IdentityEventException.class) + public void testIsOrganizationThrowsException() throws IdentityEventException { + + try (MockedStatic mockedOrgManagementUtil = + mockStatic(OrganizationManagementUtil.class)) { + + mockedOrgManagementUtil.when(() -> OrganizationManagementUtil.isOrganization(SAMPLE_TENANT_DOMAIN)) + .thenThrow(new OrganizationManagementException("Organization management error")); + + NotificationUtil.isOrganization(SAMPLE_TENANT_DOMAIN); + } + } + + @DataProvider(name = "getOrganizationUUIDDataProvider") + public Object[][] getOrganizationUUIDDataProvider() { + + return new Object[][] { + // {tenantExists, associatedOrgUUID, expectedResult} + {true, SAMPLE_ORG_UUID, SAMPLE_ORG_UUID}, + {false, null, null}, + {true, null, null} + }; + } + + @Test(dataProvider = "getOrganizationUUIDDataProvider") + public void testGetOrganizationUUID(boolean tenantExists, String associatedOrgUUID, String expectedResult) + throws Exception { + + try (MockedStatic mockedDataHolder = + mockStatic(NotificationHandlerDataHolder.class); + MockedStatic mockedIdentityTenantUtil = + mockStatic(IdentityTenantUtil.class)) { + + NotificationHandlerDataHolder mockDataHolderInstance = mock(NotificationHandlerDataHolder.class); + RealmService mockRealmService = mock(RealmService.class); + TenantManager mockTenantManager = mock(TenantManager.class); + + mockedDataHolder.when(NotificationHandlerDataHolder::getInstance).thenReturn(mockDataHolderInstance); + when(mockDataHolderInstance.getRealmService()).thenReturn(mockRealmService); + mockedIdentityTenantUtil.when(() -> IdentityTenantUtil.getTenantId(SAMPLE_TENANT_DOMAIN)) + .thenReturn(SAMPLE_TENANT_ID); + when(mockRealmService.getTenantManager()).thenReturn(mockTenantManager); + + if (tenantExists) { + Tenant mockTenant = mock(Tenant.class); + when(mockTenant.getAssociatedOrganizationUUID()).thenReturn(associatedOrgUUID); + when(mockTenantManager.getTenant(SAMPLE_TENANT_ID)).thenReturn(mockTenant); + } else { + when(mockTenantManager.getTenant(SAMPLE_TENANT_ID)).thenReturn(null); + } + + String result = NotificationUtil.getOrganizationUUID(SAMPLE_TENANT_DOMAIN); + assertEquals(result, expectedResult); + } + } + + @Test(expectedExceptions = IdentityEventException.class) + public void testGetOrganizationUUIDThrowsException() throws Exception { + + try (MockedStatic mockedDataHolder = + mockStatic(NotificationHandlerDataHolder.class); + MockedStatic mockedIdentityTenantUtil = + mockStatic(IdentityTenantUtil.class)) { + + NotificationHandlerDataHolder mockDataHolderInstance = mock(NotificationHandlerDataHolder.class); + RealmService mockRealmService = mock(RealmService.class); + TenantManager mockTenantManager = mock(TenantManager.class); + + mockedDataHolder.when(NotificationHandlerDataHolder::getInstance).thenReturn(mockDataHolderInstance); + when(mockDataHolderInstance.getRealmService()).thenReturn(mockRealmService); + mockedIdentityTenantUtil.when(() -> IdentityTenantUtil.getTenantId(SAMPLE_TENANT_DOMAIN)) + .thenReturn(SAMPLE_TENANT_ID); + when(mockRealmService.getTenantManager()).thenReturn(mockTenantManager); + when(mockTenantManager.getTenant(SAMPLE_TENANT_ID)) + .thenThrow(new UserStoreException("User store error")); + + NotificationUtil.getOrganizationUUID(SAMPLE_TENANT_DOMAIN); + } + } } From e923c8ec8011195d79f86b9b5054957f956c30d3 Mon Sep 17 00:00:00 2001 From: VihangaMunasinghe Date: Tue, 7 Apr 2026 11:12:31 +0530 Subject: [PATCH 2/6] Refactor organization UUID retrieval to primary tenant domain resolution and add primaryTenantDomain to push notification data if tenant org flow --- .../notification/PushNotificationHandler.java | 25 ++++++++++++++++++- .../notification/util/NotificationUtil.java | 25 ++++++++----------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/PushNotificationHandler.java b/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/PushNotificationHandler.java index 4df10bb6d..db08d60b2 100644 --- a/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/PushNotificationHandler.java +++ b/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/PushNotificationHandler.java @@ -19,6 +19,7 @@ package org.wso2.carbon.identity.event.handler.notification; import org.apache.commons.lang.StringUtils; +import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.identity.base.IdentityRuntimeException; import org.wso2.carbon.identity.core.bean.context.MessageContext; import org.wso2.carbon.identity.event.IdentityEventConstants; @@ -267,10 +268,31 @@ private PushNotificationData buildPushNotificationData(Event event) throws Ident } String organizationId = null; + String primaryTenantDomain = null; + if (NotificationUtil.isOrganization(tenantDomain)) { - organizationId = NotificationUtil.getOrganizationUUID(tenantDomain); + LOG.debug("Tenant domain is an organization."); + try { + organizationId = NotificationHandlerDataHolder.getInstance() + .getOrganizationManager().resolveOrganizationId(tenantDomain); + } catch (OrganizationManagementException e) { + if (LOG.isDebugEnabled()) { + LOG.debug("Error while resolving organization ID for tenant domain: " + tenantDomain, e); + } + throw new IdentityEventException(e.getMessage(), e); + } + + /* If an accessing organization ID is present, this flow is for a tenant organization path + * (/t/{tenant-domain}/o/{org-id}). + * Therefore, include the primary tenant domain in the PushNotificationData. */ + if (StringUtils.isNotEmpty(PrivilegedCarbonContext.getThreadLocalCarbonContext() + .getAccessingOrganizationId())) { + LOG.debug("Adding primary tenant domain to the push notification data."); + primaryTenantDomain = NotificationUtil.getPrimaryTenantDomain(organizationId); + } } else { // If tenant user, organizationName is null. + LOG.debug("Tenant domain is not an organization."); organizationName = null; } @@ -285,6 +307,7 @@ private PushNotificationData buildPushNotificationData(Event event) throws Ident .setTenantDomain(tenantDomain) .setOrganizationId(organizationId) .setOrganizationName(organizationName) + .setPrimaryTenantDomain(primaryTenantDomain) .setUserStoreDomain((String) eventProperties.get( IdentityEventConstants.EventProperty.USER_STORE_DOMAIN)) .setApplicationName((String) eventProperties.get(IdentityEventConstants.EventProperty.APPLICATION_NAME)) diff --git a/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtil.java b/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtil.java index a194e0a8f..cc62eddc8 100644 --- a/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtil.java +++ b/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtil.java @@ -886,27 +886,22 @@ public static boolean isOrganization(String tenetDomain) throws IdentityEventExc } /** - * Get the organization UUID for the given tenant domain. + * Get the primary tenant domain of the given organization ID. * - * @param tenantDomain Tenant domain. - * @return Organization UUID for the given tenant domain, or null if the tenant domain does not belong to an organization. - * @throws IdentityEventException Error while retrieving the organization UUID for the given tenant domain. + * @param organizationId Organization ID. + * @return Primary tenant domain. + * @throws IdentityEventException If an error occurred while getting the primary tenant domain. */ - public static String getOrganizationUUID(String tenantDomain) throws IdentityEventException { + public static String getPrimaryTenantDomain(String organizationId) throws IdentityEventException { - String organizationUUID = null; try { - RealmService realmService = NotificationHandlerDataHolder.getInstance().getRealmService(); - int tenantId = IdentityTenantUtil.getTenantId(tenantDomain); - Tenant tenant = realmService.getTenantManager().getTenant(tenantId); - if (tenant == null) { - return null; - } - organizationUUID = tenant.getAssociatedOrganizationUUID(); - } catch (UserStoreException e) { + OrganizationManager organizationManager = NotificationHandlerDataHolder.getInstance() + .getOrganizationManager(); + String primaryOrgId = organizationManager.getPrimaryOrganizationId(organizationId); + return organizationManager.resolveTenantDomain(primaryOrgId); + } catch (OrganizationManagementException e) { throw new IdentityEventException(e.getMessage(), e); } - return organizationUUID; } /** From 53d1369415add0c38f33cc48f7e0c6873e7f52c9 Mon Sep 17 00:00:00 2001 From: VihangaMunasinghe Date: Tue, 7 Apr 2026 11:12:51 +0530 Subject: [PATCH 3/6] Add tests for primary tenant domain retrieval and event handling for organization tenants --- .../PushNotificationHandlerTest.java | 156 ++++++++++++++++++ .../util/NotificationUtilTest.java | 106 ++++++------ 2 files changed, 203 insertions(+), 59 deletions(-) diff --git a/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/test/java/org/wso2/carbon/identity/event/handler/notification/PushNotificationHandlerTest.java b/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/test/java/org/wso2/carbon/identity/event/handler/notification/PushNotificationHandlerTest.java index a83208556..7bade3b62 100644 --- a/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/test/java/org/wso2/carbon/identity/event/handler/notification/PushNotificationHandlerTest.java +++ b/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/test/java/org/wso2/carbon/identity/event/handler/notification/PushNotificationHandlerTest.java @@ -21,14 +21,17 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.MockitoAnnotations; +import org.testng.Assert; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.wso2.carbon.context.CarbonContext; +import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils; import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.event.IdentityEventException; @@ -757,6 +760,159 @@ public void testHandleEventWithProviderNameVariations() throws Exception { eq("carbon.super")); } + @Test + public void testHandleEventOrganizationTenantWithAccessingOrgId() throws Exception { + + String orgTenantDomain = "org-tenant"; + String orgId = "org-uuid-123"; + String primaryOrgTenantDomain = "primary-tenant"; + + Event event = createPushNotificationEvent("FCM", orgTenantDomain); + + try (MockedStatic mockedDataHolder = mockStatic( + NotificationHandlerDataHolder.class); + MockedStatic mockedNotificationUtil = mockStatic(NotificationUtil.class); + MockedStatic mockedPrivilegedCarbonContext = + mockStatic(PrivilegedCarbonContext.class)) { + + mockedDataHolder.when(NotificationHandlerDataHolder::getInstance) + .thenReturn(notificationHandlerDataHolder); + when(notificationHandlerDataHolder.getOrganizationManager()).thenReturn(organizationManager); + when(organizationManager.resolveOrganizationId(orgTenantDomain)).thenReturn(orgId); + + mockedNotificationUtil.when(() -> NotificationUtil.resolveHumanReadableOrganizationName(anyString())) + .thenReturn(SAMPLE_ORGANIZATION_NAME); + mockedNotificationUtil.when(() -> NotificationUtil.isOrganization(orgTenantDomain)) + .thenReturn(true); + mockedNotificationUtil.when(() -> NotificationUtil.getPrimaryTenantDomain(orgId)) + .thenReturn(primaryOrgTenantDomain); + + PrivilegedCarbonContext mockPrivilegedContext = mock(PrivilegedCarbonContext.class); + mockedPrivilegedCarbonContext.when(PrivilegedCarbonContext::getThreadLocalCarbonContext) + .thenReturn(mockPrivilegedContext); + when(mockPrivilegedContext.getAccessingOrganizationId()).thenReturn("accessing-org-id"); + + when(notificationHandlerDataHolder.getNotificationSenderManagementService()) + .thenReturn(notificationSenderManagementService); + PushSenderDTO sender = new PushSenderDTO(); + sender.setName("PushPublisher"); + sender.setProvider("FCM"); + sender.setProviderId("fcm-provider-id"); + List pushSenders = new ArrayList<>(); + pushSenders.add(sender); + when(notificationSenderManagementService.getPushSenders(true)).thenReturn(pushSenders); + when(notificationHandlerDataHolder.getPushProvider("FCM")).thenReturn(pushProvider); + + pushNotificationHandler.handleEvent(event); + + ArgumentCaptor captor = ArgumentCaptor.forClass(PushNotificationData.class); + verify(pushProvider).sendNotification(captor.capture(), any(PushSenderData.class), + eq(orgTenantDomain)); + PushNotificationData data = captor.getValue(); + Assert.assertEquals(data.getOrganizationId(), orgId); + Assert.assertEquals(data.getPrimaryTenantDomain(), primaryOrgTenantDomain); + Assert.assertEquals(data.getOrganizationName(), SAMPLE_ORGANIZATION_NAME); + } + } + + @Test + public void testHandleEventOrganizationTenantWithoutAccessingOrgId() throws Exception { + + String orgTenantDomain = "org-tenant"; + String orgId = "org-uuid-123"; + + Event event = createPushNotificationEvent("FCM", orgTenantDomain); + + try (MockedStatic mockedDataHolder = mockStatic( + NotificationHandlerDataHolder.class); + MockedStatic mockedNotificationUtil = mockStatic(NotificationUtil.class); + MockedStatic mockedPrivilegedCarbonContext = + mockStatic(PrivilegedCarbonContext.class)) { + + mockedDataHolder.when(NotificationHandlerDataHolder::getInstance) + .thenReturn(notificationHandlerDataHolder); + when(notificationHandlerDataHolder.getOrganizationManager()).thenReturn(organizationManager); + when(organizationManager.resolveOrganizationId(orgTenantDomain)).thenReturn(orgId); + + mockedNotificationUtil.when(() -> NotificationUtil.resolveHumanReadableOrganizationName(anyString())) + .thenReturn(SAMPLE_ORGANIZATION_NAME); + mockedNotificationUtil.when(() -> NotificationUtil.isOrganization(orgTenantDomain)) + .thenReturn(true); + + PrivilegedCarbonContext mockPrivilegedContext = mock(PrivilegedCarbonContext.class); + mockedPrivilegedCarbonContext.when(PrivilegedCarbonContext::getThreadLocalCarbonContext) + .thenReturn(mockPrivilegedContext); + when(mockPrivilegedContext.getAccessingOrganizationId()).thenReturn(null); + + when(notificationHandlerDataHolder.getNotificationSenderManagementService()) + .thenReturn(notificationSenderManagementService); + PushSenderDTO sender = new PushSenderDTO(); + sender.setName("PushPublisher"); + sender.setProvider("FCM"); + sender.setProviderId("fcm-provider-id"); + List pushSenders = new ArrayList<>(); + pushSenders.add(sender); + when(notificationSenderManagementService.getPushSenders(true)).thenReturn(pushSenders); + when(notificationHandlerDataHolder.getPushProvider("FCM")).thenReturn(pushProvider); + + pushNotificationHandler.handleEvent(event); + + ArgumentCaptor captor = ArgumentCaptor.forClass(PushNotificationData.class); + verify(pushProvider).sendNotification(captor.capture(), any(PushSenderData.class), + eq(orgTenantDomain)); + PushNotificationData data = captor.getValue(); + Assert.assertEquals(data.getOrganizationId(), orgId); + Assert.assertNull(data.getPrimaryTenantDomain()); + Assert.assertEquals(data.getOrganizationName(), SAMPLE_ORGANIZATION_NAME); + } + } + + /** + * Test that when the tenant domain is not an organization, the built PushNotificationData has + * null organizationId, organizationName, and primaryTenantDomain. + */ + @Test + public void testHandleEventNonOrganizationTenant() throws Exception { + + Event event = createPushNotificationEvent("FCM", "carbon.super"); + + try (MockedStatic mockedDataHolder = mockStatic( + NotificationHandlerDataHolder.class); + MockedStatic mockedNotificationUtil = mockStatic(NotificationUtil.class)) { + + mockedDataHolder.when(NotificationHandlerDataHolder::getInstance) + .thenReturn(notificationHandlerDataHolder); + when(notificationHandlerDataHolder.getOrganizationManager()).thenReturn(organizationManager); + when(organizationManager.resolveOrganizationId(anyString())).thenReturn("orgId"); + + mockedNotificationUtil.when(() -> NotificationUtil.resolveHumanReadableOrganizationName(anyString())) + .thenReturn(SAMPLE_ORGANIZATION_NAME); + mockedNotificationUtil.when(() -> NotificationUtil.isOrganization("carbon.super")) + .thenReturn(false); + + when(notificationHandlerDataHolder.getNotificationSenderManagementService()) + .thenReturn(notificationSenderManagementService); + PushSenderDTO sender = new PushSenderDTO(); + sender.setName("PushPublisher"); + sender.setProvider("FCM"); + sender.setProviderId("fcm-provider-id"); + List pushSenders = new ArrayList<>(); + pushSenders.add(sender); + when(notificationSenderManagementService.getPushSenders(true)).thenReturn(pushSenders); + when(notificationHandlerDataHolder.getPushProvider("FCM")).thenReturn(pushProvider); + + pushNotificationHandler.handleEvent(event); + + ArgumentCaptor captor = ArgumentCaptor.forClass(PushNotificationData.class); + verify(pushProvider).sendNotification(captor.capture(), any(PushSenderData.class), + eq("carbon.super")); + PushNotificationData data = captor.getValue(); + Assert.assertNull(data.getOrganizationId()); + Assert.assertNull(data.getOrganizationName()); + Assert.assertNull(data.getPrimaryTenantDomain()); + } + } + // ==================== Helper Methods ==================== /** diff --git a/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/test/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtilTest.java b/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/test/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtilTest.java index f6088dde2..1394608ee 100644 --- a/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/test/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtilTest.java +++ b/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/test/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtilTest.java @@ -43,20 +43,17 @@ import org.wso2.carbon.identity.core.URLBuilderException; import org.wso2.carbon.identity.core.internal.component.IdentityCoreServiceComponent; import org.wso2.carbon.identity.core.util.IdentityConfigParser; -import org.wso2.carbon.identity.core.util.IdentityTenantUtil; import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.event.IdentityEventConstants; import org.wso2.carbon.identity.event.event.Event; import org.wso2.carbon.identity.event.handler.notification.NotificationConstants; import org.wso2.carbon.identity.event.handler.notification.internal.NotificationHandlerDataHolder; import org.wso2.carbon.identity.event.IdentityEventException; +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.exception.OrganizationManagementServerException; import org.wso2.carbon.identity.organization.management.service.util.OrganizationManagementUtil; import org.wso2.carbon.identity.organization.management.service.util.Utils; -import org.wso2.carbon.user.api.Tenant; -import org.wso2.carbon.user.core.tenant.TenantManager; -import org.wso2.carbon.user.api.UserStoreException; -import org.wso2.carbon.user.core.service.RealmService; import org.wso2.carbon.utils.CarbonUtils; import org.wso2.carbon.utils.ConfigurationContextService; @@ -568,70 +565,61 @@ public void testIsOrganizationThrowsException() throws IdentityEventException { } } - @DataProvider(name = "getOrganizationUUIDDataProvider") - public Object[][] getOrganizationUUIDDataProvider() { + /** + * Test getPrimaryTenantDomain returns the correct tenant domain when both + * getPrimaryOrganizationId and resolveTenantDomain succeed. + */ + @Test + public void testGetPrimaryTenantDomain() throws Exception { - return new Object[][] { - // {tenantExists, associatedOrgUUID, expectedResult} - {true, SAMPLE_ORG_UUID, SAMPLE_ORG_UUID}, - {false, null, null}, - {true, null, null} - }; - } + try (MockedStatic mockedDataHolder = + mockStatic(NotificationHandlerDataHolder.class)) { - @Test(dataProvider = "getOrganizationUUIDDataProvider") - public void testGetOrganizationUUID(boolean tenantExists, String associatedOrgUUID, String expectedResult) - throws Exception { + NotificationHandlerDataHolder mockDataHolder = mock(NotificationHandlerDataHolder.class); + OrganizationManager mockOrgManager = mock(OrganizationManager.class); - try (MockedStatic mockedDataHolder = - mockStatic(NotificationHandlerDataHolder.class); - MockedStatic mockedIdentityTenantUtil = - mockStatic(IdentityTenantUtil.class)) { - - NotificationHandlerDataHolder mockDataHolderInstance = mock(NotificationHandlerDataHolder.class); - RealmService mockRealmService = mock(RealmService.class); - TenantManager mockTenantManager = mock(TenantManager.class); - - mockedDataHolder.when(NotificationHandlerDataHolder::getInstance).thenReturn(mockDataHolderInstance); - when(mockDataHolderInstance.getRealmService()).thenReturn(mockRealmService); - mockedIdentityTenantUtil.when(() -> IdentityTenantUtil.getTenantId(SAMPLE_TENANT_DOMAIN)) - .thenReturn(SAMPLE_TENANT_ID); - when(mockRealmService.getTenantManager()).thenReturn(mockTenantManager); - - if (tenantExists) { - Tenant mockTenant = mock(Tenant.class); - when(mockTenant.getAssociatedOrganizationUUID()).thenReturn(associatedOrgUUID); - when(mockTenantManager.getTenant(SAMPLE_TENANT_ID)).thenReturn(mockTenant); - } else { - when(mockTenantManager.getTenant(SAMPLE_TENANT_ID)).thenReturn(null); - } + mockedDataHolder.when(NotificationHandlerDataHolder::getInstance).thenReturn(mockDataHolder); + when(mockDataHolder.getOrganizationManager()).thenReturn(mockOrgManager); + when(mockOrgManager.getPrimaryOrganizationId(SAMPLE_ORG_UUID)).thenReturn("primary-org-id"); + when(mockOrgManager.resolveTenantDomain("primary-org-id")).thenReturn(SAMPLE_TENANT_DOMAIN); - String result = NotificationUtil.getOrganizationUUID(SAMPLE_TENANT_DOMAIN); - assertEquals(result, expectedResult); + String result = NotificationUtil.getPrimaryTenantDomain(SAMPLE_ORG_UUID); + assertEquals(result, SAMPLE_TENANT_DOMAIN); } } + /** + * Test getPrimaryTenantDomain throws IdentityEventException when + * resolveTenantDomain fails with OrganizationManagementException. + */ @Test(expectedExceptions = IdentityEventException.class) - public void testGetOrganizationUUIDThrowsException() throws Exception { + public void testGetPrimaryTenantDomainThrowsWhenResolveTenantDomainFails() + throws Exception { try (MockedStatic mockedDataHolder = - mockStatic(NotificationHandlerDataHolder.class); - MockedStatic mockedIdentityTenantUtil = - mockStatic(IdentityTenantUtil.class)) { - - NotificationHandlerDataHolder mockDataHolderInstance = mock(NotificationHandlerDataHolder.class); - RealmService mockRealmService = mock(RealmService.class); - TenantManager mockTenantManager = mock(TenantManager.class); - - mockedDataHolder.when(NotificationHandlerDataHolder::getInstance).thenReturn(mockDataHolderInstance); - when(mockDataHolderInstance.getRealmService()).thenReturn(mockRealmService); - mockedIdentityTenantUtil.when(() -> IdentityTenantUtil.getTenantId(SAMPLE_TENANT_DOMAIN)) - .thenReturn(SAMPLE_TENANT_ID); - when(mockRealmService.getTenantManager()).thenReturn(mockTenantManager); - when(mockTenantManager.getTenant(SAMPLE_TENANT_ID)) - .thenThrow(new UserStoreException("User store error")); - - NotificationUtil.getOrganizationUUID(SAMPLE_TENANT_DOMAIN); + mockStatic(NotificationHandlerDataHolder.class)) { + + NotificationHandlerDataHolder mockDataHolder = mock(NotificationHandlerDataHolder.class); + OrganizationManager mockOrgManager = mock(OrganizationManager.class); + + mockedDataHolder.when(NotificationHandlerDataHolder::getInstance).thenReturn(mockDataHolder); + when(mockDataHolder.getOrganizationManager()).thenReturn(mockOrgManager); + when(mockOrgManager.getPrimaryOrganizationId(SAMPLE_ORG_UUID)).thenReturn("primary-org-id"); + when(mockOrgManager.resolveTenantDomain("primary-org-id")) + .thenThrow(new OrganizationManagementServerException("Error resolving tenant domain")); + + NotificationUtil.getPrimaryTenantDomain(SAMPLE_ORG_UUID); } } + + @DataProvider(name = "getOrganizationUUIDDataProvider") + public Object[][] getOrganizationUUIDDataProvider() { + + return new Object[][] { + // {tenantExists, associatedOrgUUID, expectedResult} + {true, SAMPLE_ORG_UUID, SAMPLE_ORG_UUID}, + {false, null, null}, + {true, null, null} + }; + } } From 36d95977765c98743c5479a26c872091ef50b275 Mon Sep 17 00:00:00 2001 From: VihangaMunasinghe Date: Tue, 7 Apr 2026 11:13:40 +0530 Subject: [PATCH 4/6] Update carbon kernel version to 4.12.29 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4221dc1bc..687b88ce0 100644 --- a/pom.xml +++ b/pom.xml @@ -560,7 +560,7 @@ [3.2.0,4.0.0) - 4.10.126 + 4.12.29 4.9.10 [4.5.0, 5.0.0) From 7970738d7b18c7176e1ff45bd513552bb9de1617 Mon Sep 17 00:00:00 2001 From: VihangaMunasinghe Date: Tue, 7 Apr 2026 12:46:23 +0530 Subject: [PATCH 5/6] Fix typo in tenant domain parameter name in isOrganization method --- .../handler/notification/util/NotificationUtil.java | 6 +++--- .../notification/util/NotificationUtilTest.java | 11 ----------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtil.java b/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtil.java index cc62eddc8..83b281d98 100644 --- a/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtil.java +++ b/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtil.java @@ -872,14 +872,14 @@ public static String resolveHumanReadableOrganizationName(String tenantDomain) t /** * Check whether the given tenant domain belongs to an organization. * - * @param tenetDomain Tenant domain. + * @param tenantDomain Tenant domain. * @return true if the tenant domain belongs to an organization, false otherwise. * @throws IdentityEventException Error while checking whether the tenant domain belongs to an organization. */ - public static boolean isOrganization(String tenetDomain) throws IdentityEventException { + public static boolean isOrganization(String tenantDomain) throws IdentityEventException { try { - return OrganizationManagementUtil.isOrganization(tenetDomain); + return OrganizationManagementUtil.isOrganization(tenantDomain); } catch (OrganizationManagementException e) { throw new IdentityEventException(e.getMessage(), e); } diff --git a/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/test/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtilTest.java b/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/test/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtilTest.java index 1394608ee..a9cdf9a4b 100644 --- a/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/test/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtilTest.java +++ b/components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/test/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtilTest.java @@ -611,15 +611,4 @@ public void testGetPrimaryTenantDomainThrowsWhenResolveTenantDomainFails() NotificationUtil.getPrimaryTenantDomain(SAMPLE_ORG_UUID); } } - - @DataProvider(name = "getOrganizationUUIDDataProvider") - public Object[][] getOrganizationUUIDDataProvider() { - - return new Object[][] { - // {tenantExists, associatedOrgUUID, expectedResult} - {true, SAMPLE_ORG_UUID, SAMPLE_ORG_UUID}, - {false, null, null}, - {true, null, null} - }; - } } From 213ca90fb82c637ad5d2f54e81cdf10dff5beee1 Mon Sep 17 00:00:00 2001 From: VihangaMunasinghe Date: Tue, 7 Apr 2026 12:59:37 +0530 Subject: [PATCH 6/6] Update push notification version to 1.1.4 in pom.xml --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 687b88ce0..44555fd89 100644 --- a/pom.xml +++ b/pom.xml @@ -536,7 +536,7 @@ 1.1.29 [1.0.1, 2.0.0) - 1.1.1 + 1.1.4 [1.0.0, 2.0.0) 3.0.0.wso2v4