Skip to content

Fix tenant and sub-organization differentiation logic when building the PushNotificationData in push notification handler#384

Merged
ZiyamSanthosh merged 6 commits into
wso2-extensions:masterfrom
VihangaMunasinghe:dev
Apr 7, 2026
Merged

Fix tenant and sub-organization differentiation logic when building the PushNotificationData in push notification handler#384
ZiyamSanthosh merged 6 commits into
wso2-extensions:masterfrom
VihangaMunasinghe:dev

Conversation

@VihangaMunasinghe

@VihangaMunasinghe VihangaMunasinghe commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

Purpose

The PushNotificationHandler incorrectly differentiated between tenant users and sub-organization users by comparing tenantDomain with organizationName when building the PushNotificationData object. This string comparison is unreliable because a tenant domain and organization name can differ for legitimate tenant users as well, causing tenant users to be misidentified as organization users and resulting in incorrect organization ID values being passed downstream.

GitHub Issue

Related PRs

  1. identity-notification-push: (prerequisite) Add primaryTenantDomain support to PushNotificationData identity-notification-push#23

Note: PRs marked as prerequisite must be merged before and bump the new version here.

Goals

  • Correctly identify whether a login flow belongs to a sub-organization or a tenant by using OrganizationManagementUtil.isOrganization() instead of a string comparison.
  • Retrieve the actual organization ID via OrganizationManager.resolveOrganizationId() instead of incorrectly using the tenant domain string as the organization ID.
  • Resolve the primary tenant domain for tenant-organization paths (/t/{tenant}/o/{org}) using OrganizationManager.getPrimaryOrganizationId() and resolveTenantDomain(), and pass it downstream via PushNotificationData.primaryTenantDomain.

Approach

  • Added two new utility methods to NotificationUtil:
    • isOrganization(String tenantDomain) — wraps OrganizationManagementUtil.isOrganization() with proper exception handling.
    • getPrimaryTenantDomain(String organizationId) — resolves the primary organization ID and then its tenant domain via OrganizationManager.
  • Updated PushNotificationHandler.buildPushNotificationData():
    • Uses NotificationUtil.isOrganization() instead of the flawed !tenantDomain.equals(organizationName) check.
    • Resolves the organization ID via OrganizationManager.resolveOrganizationId(tenantDomain).
    • Checks PrivilegedCarbonContext.getAccessingOrganizationId() to determine if this is a tenant-organization path, and if so, resolves and sets primaryTenantDomain on the PushNotificationData builder.
  • Added comprehensive unit tests covering all code paths for both new utility methods and the three organization/tenant branching scenarios in PushNotificationHandler.

User stories

As a sub-organization user, push notifications should correctly resolve my organization ID so that organization-specific notification configurations are applied. As a tenant user, the system should not incorrectly treat me as a sub-organization user.

Developer Checklist (Mandatory)

  • Complete the Developer Checklist in the related product-is issue to track any behavioral change or migration impact.

Release note

Fixed an issue where the push notification handler incorrectly identified tenant users as sub-organization users due to a flawed string comparison, causing incorrect organization ID resolution. Also added primary tenant domain resolution for tenant-organization paths.

Documentation

N/A — This is an internal bug fix with no user-facing configuration or API changes.

Training

N/A

Certification

N/A — No impact on certification exams. This is an internal logic fix.

Marketing

N/A

Automation tests

  • Unit tests (NotificationUtilTest)
    • testIsOrganization — Parameterized test verifying true and false return values.
    • testIsOrganizationThrowsException — Verifies OrganizationManagementException is wrapped as IdentityEventException.
    • testGetPrimaryTenantDomain — Verifies correct tenant domain is returned when getPrimaryOrganizationId and resolveTenantDomain succeed.
    • testGetPrimaryTenantDomainThrowsWhenResolveTenantDomainFails — Verifies OrganizationManagementServerException is wrapped as IdentityEventException.
  • Unit tests (PushNotificationHandlerTest)
    • testHandleEventOrganizationTenantWithAccessingOrgId — Verifies organization tenant with accessing org ID sets organizationId, organizationName, and primaryTenantDomain.
    • testHandleEventOrganizationTenantWithoutAccessingOrgId — Verifies organization tenant without accessing org ID sets organizationId and organizationName but primaryTenantDomain is null.
    • testHandleEventNonOrganizationTenant — Verifies non-organization tenant has null organizationId, organizationName, and primaryTenantDomain.
  • Integration tests
    • N/A

Security checks

Samples

N/A

Migrations (if applicable)

N/A — No migrations required. This is a logic fix with no schema or configuration changes.

Test environment

N/A

Learning

Used OrganizationManagementUtil.isOrganization() as the authoritative API for checking organization membership, OrganizationManager.resolveOrganizationId() for resolving the organization ID from a tenant domain, and OrganizationManager.getPrimaryOrganizationId() + resolveTenantDomain() for resolving the primary tenant domain of an organization.

Summary by CodeRabbit

  • New Features

    • Improved organization and tenant detection for push notifications; primary tenant domain is now tracked when available.
  • Tests

    • Added unit tests covering organization vs. tenant flows and primary tenant domain resolution.
  • Chores

    • Bumped notification and core kernel dependency versions.

@coderabbitai

coderabbitai Bot commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 058963bf-c002-4b92-984a-a4f36990e565

📥 Commits

Reviewing files that changed from the base of the PR and between 36d9597 and 213ca90.

📒 Files selected for processing (3)
  • components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtil.java
  • components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/test/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtilTest.java
  • pom.xml
🚧 Files skipped from review as they are similar to previous changes (2)
  • pom.xml
  • components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/test/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtilTest.java

📝 Walkthrough

Walkthrough

Push notification handling now uses PrivilegedCarbonContext to detect accessing organization, adds organization detection and primary-tenant-domain utilities, populates primaryTenantDomain in PushNotificationData when available, expands unit tests for organization/non-organization flows, and bumps Carbon kernel and push provider versions in pom.xml.

Changes

Cohort / File(s) Summary
Push notification handler
components/event-handler-notification/.../PushNotificationHandler.java
Integrates PrivilegedCarbonContext for accessing organization ID; uses NotificationUtil.isOrganization(...) to detect organizations; resolves organizationId and conditionally resolves/sets primaryTenantDomain on PushNotificationData; adds debug logging and exception wrapping.
Notification utilities
components/event-handler-notification/.../util/NotificationUtil.java
Adds isOrganization(String tenantDomain) and getPrimaryTenantDomain(String organizationId) that delegate to organization management APIs and wrap OrganizationManagementException in IdentityEventException.
Unit tests
components/event-handler-notification/.../PushNotificationHandlerTest.java, components/event-handler-notification/.../util/NotificationUtilTest.java
Adds tests covering organization vs non-organization flows, accessing-org-id presence/absence, primary tenant domain resolution, and exception paths; uses ArgumentCaptor and static mocking of organization APIs.
Build configuration
pom.xml
Bumps identity.notification.push.version (1.1.1 → 1.1.4) and carbon.kernel.version (4.10.126 → 4.12.29), updating resolved Carbon/kernel dependency versions.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant Handler as PushNotificationHandler
    participant Context as PrivilegedCarbonContext
    participant Util as NotificationUtil
    participant OrgMgr as OrganizationManager
    participant Provider as PushProvider

    Client->>Handler: handleEvent(event)
    Handler->>Context: getThreadLocalCarbonContext()
    Context-->>Handler: carbonContext
    Handler->>Util: isOrganization(tenantDomain)
    Util->>OrgMgr: OrganizationManagementUtil.isOrganization(...)
    OrgMgr-->>Util: boolean
    Util-->>Handler: isOrganization

    alt organization tenant
        Handler->>Util: resolveOrganizationId(tenantDomain)
        Util->>OrgMgr: resolve organization id
        OrgMgr-->>Util: organizationId
        Util-->>Handler: organizationId
        Handler->>Context: getAccessingOrganizationId()
        Context-->>Handler: accessingOrgId

        alt accessingOrgId present
            Handler->>Util: getPrimaryTenantDomain(organizationId)
            Util->>OrgMgr: getPrimaryOrganizationId + resolveTenantDomain(...)
            OrgMgr-->>Util: primaryTenantDomain
            Util-->>Handler: primaryTenantDomain
        end
    end

    Handler->>Provider: sendNotification(pushNotificationData)
    Provider-->>Handler: result
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly Related PRs

Suggested Reviewers

  • SujanSanjula96
  • ZiyamSanthosh
  • wso2-engineering
  • jenkins-is-staging

Poem

🐰 I sniffed the thread-local breeze,
Found org IDs beneath the trees,
Resolved their primary tenant home,
Sent notifications smartly, never alone.
Tests hopped by, carrots in tow—hooray! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: fixing tenant/sub-organization differentiation logic in the push notification handler when building PushNotificationData.
Description check ✅ Passed The PR description comprehensively covers all required sections: Purpose clearly states the problem, Goals outline the solutions, Approach details implementation with new methods and logic changes, comprehensive test coverage is described, and security/migration/documentation sections are properly addressed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment on lines +269 to +271
String organizationId = null;
if (!tenantDomain.equals(organizationName)) {
organizationId = tenantDomain;
if (NotificationUtil.isOrganization(tenantDomain)) {
organizationId = NotificationUtil.getOrganizationUUID(tenantDomain);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log Improvement Suggestion No: 1

Suggested change
String organizationId = null;
if (!tenantDomain.equals(organizationName)) {
organizationId = tenantDomain;
if (NotificationUtil.isOrganization(tenantDomain)) {
organizationId = NotificationUtil.getOrganizationUUID(tenantDomain);
String organizationId = null;
if (NotificationUtil.isOrganization(tenantDomain)) {
log.debug("Tenant domain is an organization. Retrieving organization UUID.");
organizationId = NotificationUtil.getOrganizationUUID(tenantDomain);

Comment on lines 271 to 274
organizationId = NotificationUtil.getOrganizationUUID(tenantDomain);
} else {
// If tenant user, organizationName is null.
organizationName = null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log Improvement Suggestion No: 2

Suggested change
organizationId = NotificationUtil.getOrganizationUUID(tenantDomain);
} else {
// If tenant user, organizationName is null.
organizationName = null;
organizationId = NotificationUtil.getOrganizationUUID(tenantDomain);
} else {
// If tenant user, organizationName is null.
log.debug("Tenant domain is not an organization. Setting organizationName to null.");
organizationName = null;

Comment on lines +878 to +882
*/
public static boolean isOrganization(String tenetDomain) throws IdentityEventException {

try {
return OrganizationManagementUtil.isOrganization(tenetDomain);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log Improvement Suggestion No: 3

Suggested change
*/
public static boolean isOrganization(String tenetDomain) throws IdentityEventException {
try {
return OrganizationManagementUtil.isOrganization(tenetDomain);
public static boolean isOrganization(String tenetDomain) throws IdentityEventException {
if (log.isDebugEnabled()) {
log.debug("Checking if tenant domain belongs to an organization: " + tenetDomain);
}
try {
return OrganizationManagementUtil.isOrganization(tenetDomain);

Comment on lines +894 to +896
*/
public static String getOrganizationUUID(String tenantDomain) throws IdentityEventException {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log Improvement Suggestion No: 4

Suggested change
*/
public static String getOrganizationUUID(String tenantDomain) throws IdentityEventException {
public static String getOrganizationUUID(String tenantDomain) throws IdentityEventException {
if (log.isDebugEnabled()) {
log.debug("Retrieving organization UUID for tenant domain: " + tenantDomain);
}
String organizationUUID = null;

@wso2-engineering wso2-engineering Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Agent Log Improvement Checklist

⚠️ Warning: AI-Generated Review Comments

  • The log-related comments and suggestions in this review were generated by an AI tool to assist with identifying potential improvements. Purpose of reviewing the code for log improvements is to improve the troubleshooting capabilities of our products.
  • Please make sure to manually review and validate all suggestions before applying any changes. Not every code suggestion would make sense or add value to our purpose. Therefore, you have the freedom to decide which of the suggestions are helpful.

✅ Before merging this pull request:

  • Review all AI-generated comments for accuracy and relevance.
  • Complete and verify the table below. We need your feedback to measure the accuracy of these suggestions and the value they add. If you are rejecting a certain code suggestion, please mention the reason briefly in the suggestion for us to capture it.
Comment Accepted (Y/N) Reason
#### Log Improvement Suggestion No: 1
#### Log Improvement Suggestion No: 2
#### Log Improvement Suggestion No: 3
#### Log Improvement Suggestion No: 4

@codecov

codecov Bot commented Apr 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.00000% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 39.21%. Comparing base (2aae2ea) to head (213ca90).
⚠️ Report is 7 commits behind head on master.

Files with missing lines Patch % Lines
.../handler/notification/PushNotificationHandler.java 75.00% 4 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master     #384      +/-   ##
============================================
+ Coverage     39.00%   39.21%   +0.20%     
- Complexity      636      640       +4     
============================================
  Files            78       78              
  Lines          5755     5778      +23     
  Branches        872      874       +2     
============================================
+ Hits           2245     2266      +21     
- Misses         3263     3266       +3     
+ Partials        247      246       -1     
Flag Coverage Δ
unit 24.33% <84.00%> (+0.45%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@VihangaMunasinghe
VihangaMunasinghe marked this pull request as ready for review April 7, 2026 05:58

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
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)

615-623: Remove the orphaned data provider.

getOrganizationUUIDDataProvider() is not referenced by any @Test(dataProvider = ...), so it currently just adds maintenance noise.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/test/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtilTest.java`
around lines 615 - 623, Remove the orphaned DataProvider method
getOrganizationUUIDDataProvider() from the NotificationUtilTest class since it
is not referenced by any `@Test`(dataProvider = ...) and only adds maintenance
noise; delete the entire method (including its `@DataProvider` annotation and
returned Object[][]) or alternatively wire it to a test that actually needs the
cases, but prefer removing getOrganizationUUIDDataProvider() to keep the test
class clean.
components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/PushNotificationHandler.java (1)

275-283: Reuse the organization ID that handleEvent() already resolved.

handleEvent() stores ORGANIZATION_ID_PLACEHOLDER before this private method runs, so resolving the same ID again here adds a second OrganizationManager call and another avoidable failure path.

♻️ Proposed simplification
-            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);
-            }
+            organizationId = (String) eventProperties.get(
+                    NotificationConstants.EmailNotification.ORGANIZATION_ID_PLACEHOLDER);
+            if (StringUtils.isBlank(organizationId)) {
+                throw new IdentityEventException("Organization ID is not found in the event properties.");
+            }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/PushNotificationHandler.java`
around lines 275 - 283, The code redundantly calls
OrganizationManager.resolveOrganizationId(tenantDomain) inside the private
method; instead, reuse the organization ID already resolved and stored by
handleEvent() under the ORGANIZATION_ID_PLACEHOLDER key. Modify the private
method in PushNotificationHandler to read the organizationId from the
event/context using the same ORGANIZATION_ID_PLACEHOLDER (or fall back to the
resolveOrganizationId call only if the placeholder is missing) so you avoid an
extra OrganizationManager invocation and duplicate failure path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/PushNotificationHandler.java`:
- Around line 275-283: The code redundantly calls
OrganizationManager.resolveOrganizationId(tenantDomain) inside the private
method; instead, reuse the organization ID already resolved and stored by
handleEvent() under the ORGANIZATION_ID_PLACEHOLDER key. Modify the private
method in PushNotificationHandler to read the organizationId from the
event/context using the same ORGANIZATION_ID_PLACEHOLDER (or fall back to the
resolveOrganizationId call only if the placeholder is missing) so you avoid an
extra OrganizationManager invocation and duplicate failure path.

In
`@components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/test/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtilTest.java`:
- Around line 615-623: Remove the orphaned DataProvider method
getOrganizationUUIDDataProvider() from the NotificationUtilTest class since it
is not referenced by any `@Test`(dataProvider = ...) and only adds maintenance
noise; delete the entire method (including its `@DataProvider` annotation and
returned Object[][]) or alternatively wire it to a test that actually needs the
cases, but prefer removing getOrganizationUUIDDataProvider() to keep the test
class clean.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9df0c6a7-378a-4296-adbd-8b43d7398c88

📥 Commits

Reviewing files that changed from the base of the PR and between 2aae2ea and 36d9597.

📒 Files selected for processing (5)
  • components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/PushNotificationHandler.java
  • components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/main/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtil.java
  • components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/test/java/org/wso2/carbon/identity/event/handler/notification/PushNotificationHandlerTest.java
  • components/event-handler-notification/org.wso2.carbon.identity.event.handler.notification/src/test/java/org/wso2/carbon/identity/event/handler/notification/util/NotificationUtilTest.java
  • pom.xml

@jenkins-is-staging

Copy link
Copy Markdown

PR builder started
Link: https://github.com/wso2/product-is/actions/runs/24069955218

@jenkins-is-staging

Copy link
Copy Markdown

PR builder completed
Link: https://github.com/wso2/product-is/actions/runs/24069955218
Status: failure

@jenkins-is-staging

Copy link
Copy Markdown

PR builder started
Link: https://github.com/wso2/product-is/actions/runs/24069955218

@jenkins-is-staging

Copy link
Copy Markdown

PR builder completed
Link: https://github.com/wso2/product-is/actions/runs/24069955218
Status: success

@jenkins-is-staging jenkins-is-staging left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving the pull request based on the successful pr build https://github.com/wso2/product-is/actions/runs/24069955218

@ZiyamSanthosh
ZiyamSanthosh merged commit c6d6367 into wso2-extensions:master Apr 7, 2026
5 checks passed
ZiyamSanthosh added a commit that referenced this pull request Apr 8, 2026
[Sync][master -> next][#384]: Fix tenant and sub-organization differentiation logic when building the PushNotificationData in push notification handler
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants