Skip to content

Conversation

@ShanChathusanda93
Copy link
Contributor

@ShanChathusanda93 ShanChathusanda93 commented Dec 2, 2025

Changes in this PR

  • $subject
image

Summary by CodeRabbit

  • Tests

    • Added a new end-to-end integration test suite for OAuth2 authorization of sub-organization applications and expanded test setup/cleanup across org scopes.
  • New Features

    • Introduced organization-scoped REST flows for application management, OIDC inbound retrieval, organization lifecycle, and SCIM role operations.
    • Added an organization-switch OAuth2 grant constant.
  • Documentation / Resources

    • Added JSON mappings of API endpoints to required permissions and updated test configuration.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 2, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds organization-scoped REST methods and helpers (OAuth2, Org management, SCIM), a new OAuth2 grant constant, two permissions JSON resources, a TestNG entry, and a comprehensive integration test exercising sub-organization OAuth2 application and token flows.

Changes

Cohort / File(s) Summary
OAuth2 REST client (org-scoped)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java
Added org-scoped methods: createOrganizationApplication(ApplicationModel, String), getOrganizationApplication(String, String), getOIDCInboundDetailsOfOrganizationApp(String, String) and private helper getConfigOfOrganizationApp(...). Mirrors non-org endpoints, uses Bearer token.
Test abstract utilities
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java
Added addOrganizationApplication(String, ApplicationConfig, String) and getOIDCInboundDetailsOfOrganizationApplication(String, String) delegating to org-scoped REST client methods.
Organization management client
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OrgMgtRestClient.java
Added addOrganizationWithToken(String, String, String) and deleteOrganizationWithToken(String, String) to create/delete organizations using an explicit access token.
SCIM2 client (org role ops)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/SCIM2RestClient.java
Added updateOrganizationUserRole(PatchOperationRequestObject, String, String) and addOrganizationV2Roles(RoleV2, String) for org-scoped role update/creation using Bearer token.
Integration test
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2AuthorizationForSubOrganizationAppsTestCase.java
New end-to-end integration test class exercising sub-organization app flows: org creation, app creation/sharing, M2M tokens, user/role provisioning, organization-switch grant flows, opaque vs JWT tokens, introspection, API calls, and cleanup.
Constants
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java
Added public static final String OAUTH2_GRANT_TYPE_ORGANIZATION_SWITCH = "organization_switch";.
Test resources & config
modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/oauth2/management-app-authorized-apis.json, modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/oauth2/organization-app-authorized-apis.json, modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml
Added JSON mappings for management and organization API permissions; added OAuth2AuthorizationForSubOrganizationAppsTestCase entry to testng.xml.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Test as Integration Test
participant AuthServer as Authorization Server
participant OAuthClient as OAuth2RestClient
participant OrgClient as OrgMgtRestClient
participant SCIMClient as SCIM2RestClient
participant API as Management/API Resource

rect rgb(235,245,255)
Note over Test,AuthServer: Setup & configuration
end

Test->>AuthServer: request M2M token
AuthServer-->>Test: M2M token

Test->>OrgClient: addOrganizationWithToken(orgName, orgHandle, m2mToken)
OrgClient->>AuthServer: POST /o/admin/organizations (Bearer m2mToken)
AuthServer-->>OrgClient: 201 Created (orgId)
OrgClient-->>Test: orgId

Test->>OAuthClient: createOrganizationApplication(appModel, switchedAccessToken)
OAuthClient->>AuthServer: POST /o/api/server/v1/applications (Bearer switchedAccessToken)
AuthServer-->>OAuthClient: 201 Created (Location → appId)
OAuthClient-->>Test: appId

Test->>SCIMClient: addOrganizationV2Roles(role, accessToken)
SCIMClient->>AuthServer: POST /o/scim2/Roles (Bearer accessToken)
AuthServer-->>SCIMClient: 201 Created (roleId)
SCIMClient-->>Test: roleId

Test->>AuthServer: exchange for organization-switched token (grant_type=organization_switch)
AuthServer-->>Test: access token (opaque or JWT)

Test->>API: call management API (Authorization: Bearer token)
API->>AuthServer: introspect/validate token
AuthServer-->>API: introspection / claims
API-->>Test: 200 OK / resource response

Note over Test: Cleanup (delete app, roles, org)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I hopped through tokens, roles, and grants,
Sub-org apps sprouted in tidy pants,
REST calls scurried, introspection sang,
JWT dots and opaque tails rang,
A tiny rabbit cheers these integration plants.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 32.61% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add integration tests for performing authorizations for sub organization applications' directly and clearly describes the main change—a new comprehensive integration test class (OAuth2AuthorizationForSubOrganizationAppsTestCase) with supporting helper methods and resources for OAuth2 authorization workflows in sub-organization contexts.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (10)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java (1)

31-31: Consider using this constant in OrgMgtRestClient.java.

The new constant is correctly defined. However, OrgMgtRestClient.java at line 274 uses a hardcoded string "organization_switch" instead of referencing this constant:

urlParameters.add(new BasicNameValuePair(OAuth2Constant.GRANT_TYPE_NAME, "organization_switch"));

Consider updating to use the constant for consistency:

-urlParameters.add(new BasicNameValuePair(OAuth2Constant.GRANT_TYPE_NAME, "organization_switch"));
+urlParameters.add(new BasicNameValuePair(OAuth2Constant.GRANT_TYPE_NAME, OAuth2Constant.OAUTH2_GRANT_TYPE_ORGANIZATION_SWITCH));
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java (1)

272-332: Consider extracting common application configuration logic.

This method largely duplicates the logic in addApplication(ApplicationConfig) (lines 192-241). The only differences are the application name source and the REST client methods used.

Consider extracting the common application model building logic into a private helper method to reduce duplication:

private ApplicationModel buildApplicationModel(String applicationName, ApplicationConfig applicationConfig) {
    // Common configuration building logic
}

Then both methods can use this helper, with only the REST client call differing.

modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2AuthorizationCodeGrantForSubOrgAppsTestCase.java (7)

56-59: Avoid wildcard imports for clarity and to prevent namespace conflicts.

Using wildcard imports (import java.util.*; and import static org.testng.Assert.*;) can lead to ambiguity and makes it harder to track which classes are actually used.

Consider explicit imports:

-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;

100-113: Static mutable fields may cause issues in parallel test execution.

CLIENT_ID, CLIENT_SECRET, ORG_APP_CLIENT_ID, and ORG_APP_CLIENT_SECRET are static but mutated during test setup. If tests run in parallel, this could lead to race conditions.

Consider making these instance fields instead:

-    private static String CLIENT_ID;
-    private static String CLIENT_SECRET;
-    private static String ORG_APP_CLIENT_ID;
-    private static String ORG_APP_CLIENT_SECRET;
+    private String clientId;
+    private String clientSecret;
+    private String orgAppClientId;
+    private String orgAppClientSecret;

264-286: Created resources are not cleaned up after test.

The test creates an organization application (ORGANIZATION_TEST_APP_NAME) but doesn't delete it in cleanup. This could leave test artifacts and affect subsequent test runs.

Consider storing the created orgAppId as a class field and deleting it in atEnd():

// In atEnd():
if (testOrgAppId != null) {
    oAuth2RestClient.deleteApplication(testOrgAppId);
}

288-317: Created test user is not cleaned up.

Similar to the application, the user created with TEST_ORG_END_USER_USERNAME is not deleted after the test completes.

Store the user ID and delete it in the cleanup phase to maintain test isolation.


384-455: Significant code duplication between updateAuthorizedAPIs and updateOrganizationAppAuthorizedAPIs.

These two methods have nearly identical logic, differing only in the path construction and headers used. Consider extracting common logic.

private void updateAuthorizedAPIsInternal(String appId, JSONObject authorizedAPIs, 
        String apiBasePath, String appBasePath, Header[] headers) throws JSONException, IOException {
    
    RestBaseClient restBaseClient = new RestBaseClient();
    for (Iterator<String> apiNameIterator = authorizedAPIs.keys(); apiNameIterator.hasNext(); ) {
        String apiName = apiNameIterator.next();
        Object requiredScopes = authorizedAPIs.get(apiName);
        // ... common logic
    }
}

551-553: Redundant EntityUtils.consume after EntityUtils.toString.

EntityUtils.toString() already consumes the entity, making the subsequent EntityUtils.consume() call unnecessary.

         HttpResponse response = client.execute(httpPost);
         String responseString = EntityUtils.toString(response.getEntity(), "UTF-8");
-        EntityUtils.consume(response.getEntity());
         JSONParser parser = new JSONParser();

596-604: getAuthorizationCodeFromURL returns null silently on failure.

Returning null when the authorization code is not found could lead to cryptic NullPointerExceptions downstream. Consider throwing an exception with a descriptive message.

 private String getAuthorizationCodeFromURL(String location) {
 
     URI uri = URI.create(location);
     return URLEncodedUtils.parse(uri, StandardCharsets.UTF_8).stream()
             .filter(param -> "code".equals(param.getName()))
             .map(NameValuePair::getValue)
             .findFirst()
-            .orElse(null);
+            .orElseThrow(() -> new IllegalStateException(
+                "Authorization code not found in URL: " + location));
 }
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java (1)

531-541: Consider adding error handling for failed requests.

Similar to getConfig, this method doesn't validate the response status code before returning the entity. A failed request would return an error JSON that would fail deserialization upstream. While this is consistent with existing code, consider improving both.

 private String getConfigOrOrganizationApp(String appId, String inboundType,
                                           String accessToken) throws Exception {

     String endPointUrl = subOrgApplicationManagementApiBasePath + PATH_SEPARATOR + appId + INBOUND_PROTOCOLS_BASE_PATH +
             PATH_SEPARATOR + inboundType;

     try (CloseableHttpResponse response = getResponseOfHttpGet(endPointUrl,
             getHeadersWithBearerToken(accessToken))) {
+        if (response.getStatusLine().getStatusCode() >= 400) {
+            throw new RuntimeException("Failed to get inbound config. Status: " + 
+                response.getStatusLine().getStatusCode());
+        }
         return EntityUtils.toString(response.getEntity());
     }
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb17c20 and 666cb80.

📒 Files selected for processing (8)
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2AuthorizationCodeGrantForSubOrgAppsTestCase.java (1 hunks)
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java (3 hunks)
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java (8 hunks)
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OrgMgtRestClient.java (2 hunks)
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/SCIM2RestClient.java (1 hunks)
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java (1 hunks)
  • modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/oauth2/management-app-authorized-apis.json (1 hunks)
  • modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/oauth2/organization-app-authorized-apis.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java (1)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java (1)
  • OAuth2Constant (24-150)
🔇 Additional comments (13)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/SCIM2RestClient.java (1)

522-541: LGTM!

The new updateOrganizationUserRole method correctly follows the existing patterns in this class for organization-scoped operations. It properly uses the getSubOrgRolesV2Path() endpoint builder and bearer token authentication consistent with similar methods like updateUsersOfRoleV2InSubOrg.

modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/oauth2/management-app-authorized-apis.json (1)

1-35: LGTM!

The JSON configuration correctly maps management API endpoints to their required internal permissions. The structure is well-organized and follows a consistent permission naming pattern aligned with WSO2 Identity Server conventions.

modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OrgMgtRestClient.java (2)

137-154: LGTM!

The new addOrganizationWithToken method correctly follows the existing pattern of the addOrganization overloads, allowing tests to provide an externally managed token for organization creation.


210-224: LGTM!

The new deleteOrganizationWithToken method correctly mirrors the existing deleteOrganization method while accepting an externally provided access token. The HTTP 204 NO_CONTENT assertion is properly included.

modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/oauth2/organization-app-authorized-apis.json (1)

10-15: Verify the role permissions scope difference is intentional.

The /o/scim2/Roles endpoint here includes 4 permissions (view, create, update, delete), while the same endpoint in management-app-authorized-apis.json includes 7 permissions (additionally: internal_org_role_mgt_permissions_update, internal_org_role_mgt_users_update, internal_org_role_mgt_groups_update).

Please confirm this difference is intentional based on the distinct use cases of these two configuration files (organization-app vs management-app contexts).

modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java (3)

285-287: Null config fallback returns a non-organization application.

When applicationConfig is null, this method returns getBasicOAuthApplication(CALLBACK_URL) which creates a regular application, not an organization-scoped one. This may be unexpected since the method name suggests organization-specific behavior.

Consider either:

  1. Creating an organization-scoped basic application when config is null
  2. Throwing an exception if config is null for organization applications
  3. Documenting this behavior in the Javadoc

432-443: LGTM!

The new addOrganizationRole method follows the established pattern of the existing addRole method, cleanly delegating to the organization-scoped REST client method.


486-499: LGTM!

The new getOIDCInboundDetailsOfOrganizationApplication method cleanly mirrors the existing getOIDCInboundDetailsOfApplication method for organization-scoped operations.

modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2AuthorizationCodeGrantForSubOrgAppsTestCase.java (1)

115-163: LGTM - Well-structured test setup.

The setup method properly initializes HTTP clients with cookie handling, creates necessary REST clients, and establishes the organization hierarchy required for testing. The disabled redirect strategy is appropriate for OAuth flow testing where manual redirect handling is needed.

modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java (4)

170-192: LGTM - Consistent with existing pattern.

The createOrganizationApplication method follows the same pattern as createApplication, appropriately using Bearer token authentication for organization-scoped operations.


279-299: LGTM - Follows existing pattern correctly.

The getOrganizationApplication method correctly implements organization-scoped application retrieval using Bearer token authentication, consistent with the existing getApplication method pattern.


490-504: LGTM - Proper delegation to private helper.

The getOIDCInboundDetailsOfOrganizationApp method correctly delegates to getConfigOrOrganizationApp and deserializes the response, following the same pattern as getOIDCInboundDetails.


891-898: LGTM - Consistent path construction.

The getOrganizationSCIM2RoleV2Path method follows the established pattern for constructing organization-scoped paths, correctly handling both super tenant and regular tenant domains.

Comment on lines 413 to 415
assertEquals(appAuthorizedAPIsResponse.getStatusLine().getStatusCode(), HttpStatus.SC_OK,
String.format("Authorized APIs update failed for application with ID: %s for API: %s.",
appAuthorizedAPIsResponse, apiName));
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Error message logs response object instead of response body.

The error message includes appAuthorizedAPIsResponse (the response object) instead of the actual response body, which won't provide useful debugging information.

-                assertEquals(appAuthorizedAPIsResponse.getStatusLine().getStatusCode(), HttpStatus.SC_OK,
-                        String.format("Authorized APIs update failed for application with ID: %s for API: %s.",
-                                appAuthorizedAPIsResponse, apiName));
+                assertEquals(appAuthorizedAPIsResponse.getStatusLine().getStatusCode(), HttpStatus.SC_OK,
+                        String.format("Authorized APIs update failed for application with ID: %s for API: %s.",
+                                appId, apiName));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
assertEquals(appAuthorizedAPIsResponse.getStatusLine().getStatusCode(), HttpStatus.SC_OK,
String.format("Authorized APIs update failed for application with ID: %s for API: %s.",
appAuthorizedAPIsResponse, apiName));
assertEquals(appAuthorizedAPIsResponse.getStatusLine().getStatusCode(), HttpStatus.SC_OK,
String.format("Authorized APIs update failed for application with ID: %s for API: %s.",
appId, apiName));
🤖 Prompt for AI Agents
In
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2AuthorizationCodeGrantForSubOrgAppsTestCase.java
around lines 413-415, the assertion error message logs the response object
(appAuthorizedAPIsResponse) instead of the response body; update the assertion
to extract the response body string (the HTTP entity/content) from
appAuthorizedAPIsResponse and include that body in the formatted error message
so failures show the actual server response content rather than the response
object reference.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds comprehensive integration tests for OAuth2 authorization code grant flows in sub-organization contexts. The tests cover the complete workflow including organization management, application provisioning, user management, role assignment, and token exchange operations.

Key Changes

  • Added new integration test case OAuth2AuthorizationCodeGrantForSubOrgAppsTestCase to verify OAuth2 authorization code grant for sub-organization applications
  • Introduced organization-scoped operations in REST clients (OAuth2RestClient, SCIM2RestClient, OrgMgtRestClient) to support token-based authentication for organization-level operations
  • Added OAUTH2_GRANT_TYPE_ORGANIZATION_SWITCH constant to support organization switching grant type
  • Added JSON configuration files defining authorized APIs for management and organization applications

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
OAuth2AuthorizationCodeGrantForSubOrgAppsTestCase.java New comprehensive integration test covering authorization code flow for sub-organization apps, including organization creation, app provisioning, user management, and API authorization
OAuth2Constant.java Added constant for organization_switch grant type
SCIM2RestClient.java Added updateOrganizationUserRole method to support role updates with token authentication
OrgMgtRestClient.java Added token-based methods addOrganizationWithToken and deleteOrganizationWithToken for organization management
OAuth2RestClient.java Added organization-specific methods for application, role, and OIDC configuration management with token authentication
OAuth2ServiceAbstractIntegrationTest.java Added helper methods addOrganizationApplication, addOrganizationRole, and getOIDCInboundDetailsOfOrganizationApplication for organization-level operations
organization-app-authorized-apis.json Configuration file defining authorized API scopes for organization applications
management-app-authorized-apis.json Configuration file defining authorized API scopes for management applications

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}
}

private String getConfigOrOrganizationApp(String appId, String inboundType,
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

The method name getConfigOrOrganizationApp contains a typo. It should be getConfigOfOrganizationApp (using "Of" instead of "Or"). The current name suggests "or" as in a boolean operator rather than "of" which correctly expresses possession/relationship.

Copilot uses AI. Check for mistakes.
* Get Application oidc inbound configuration details with a given application id of an organization.
*
* @param appId Application ID.
* @param switchedAccessToken Authorized token to get the OIDC inbound configurations or an application.
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

The JavaDoc contains a typo. "configurations or an application" should be "configurations of an application". The word "or" should be "of".

Suggested change
* @param switchedAccessToken Authorized token to get the OIDC inbound configurations or an application.
* @param switchedAccessToken Authorized token to get the OIDC inbound configurations of an application.

Copilot uses AI. Check for mistakes.
oidcConfig.refreshToken(refreshTokenConfiguration);
}

if (applicationConfig.getAudienceList() != null && !applicationConfig.getRequestedClaimList().isEmpty()) {
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

The condition on line 313 checks !applicationConfig.getRequestedClaimList().isEmpty() but this is inconsistent with the logic. The audience list should be added to the ID token configuration when the audience list is not null, regardless of whether the requested claim list is empty. The condition should check !applicationConfig.getAudienceList().isEmpty() instead, or simply set the audience if the list is not null.

Suggested change
if (applicationConfig.getAudienceList() != null && !applicationConfig.getRequestedClaimList().isEmpty()) {
if (applicationConfig.getAudienceList() != null && !applicationConfig.getAudienceList().isEmpty()) {

Copilot uses AI. Check for mistakes.
Comment on lines 212 to 216
* Create a v2 role in the organization.
*
* @param role an instance of RoleV2.
* @param accessToken Authorized token to create v2 roles in an organization.
* @return the roleID.
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

[nitpick] JavaDoc inconsistency: The documentation style is inconsistent. This method uses lowercase "v2" while other similar methods use "V2" (uppercase). For consistency with the existing createV2Roles method (line 195), this should say "Create a V2 role in the organization." The return value documentation should also say "the role ID" (with space) to match the pattern in line 198.

Suggested change
* Create a v2 role in the organization.
*
* @param role an instance of RoleV2.
* @param accessToken Authorized token to create v2 roles in an organization.
* @return the roleID.
* Create a V2 role in the organization.
*
* @param role an instance of RoleV2.
* @param accessToken Authorized token to create V2 roles in an organization.
* @return the role ID.

Copilot uses AI. Check for mistakes.
String switchedAccessToken)
throws Exception {

return restClient.getOIDCInboundDetailsOfOrganizationApp(appId, switchedAccessToken);
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Double space detected: There are two spaces between appId, and switchedAccessToken in the method call. This should be a single space for consistency with coding standards.

Suggested change
return restClient.getOIDCInboundDetailsOfOrganizationApp(appId, switchedAccessToken);
return restClient.getOIDCInboundDetailsOfOrganizationApp(appId, switchedAccessToken);

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

No two spaces

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +489 to +711
headerList[1] = new BasicHeader(AUTHORIZATION_ATTRIBUTE, BEARER_TOKEN_AUTHORIZATION_ATTRIBUTE
+ switchedM2MToken);
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

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

[nitpick] Extra space after the token in the Authorization header construction. The code currently has two spaces between "BEARER_TOKEN_AUTHORIZATION_ATTRIBUTE" and "switchedM2MToken", but only one is needed.

Suggested change
headerList[1] = new BasicHeader(AUTHORIZATION_ATTRIBUTE, BEARER_TOKEN_AUTHORIZATION_ATTRIBUTE
+ switchedM2MToken);
headerList[1] = new BasicHeader(AUTHORIZATION_ATTRIBUTE, BEARER_TOKEN_AUTHORIZATION_ATTRIBUTE.trim()
+ " " + switchedM2MToken.trim());

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here [1] is having only one space

[1]

* Get Application oidc inbound configuration details with a given application id of an organization.
*
* @param appId Application ID.
* @param switchedAccessToken Authorized token to get the OIDC inbound configurations or an application.
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

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

Typo in the comment: 'or' should be 'of'. The comment reads "Authorized token to get the OIDC inbound configurations or an application" but should be "Authorized token to get the OIDC inbound configurations of an application".

Suggested change
* @param switchedAccessToken Authorized token to get the OIDC inbound configurations or an application.
* @param switchedAccessToken Authorized token to get the OIDC inbound configurations of an application.

Copilot uses AI. Check for mistakes.
@ShanChathusanda93 ShanChathusanda93 force-pushed the sub-org-tenant-app-branch branch from 666cb80 to c176ccd Compare December 4, 2025 21:33
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (8)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2AuthorizationCodeGrantForSubOrgAppsTestCase.java (4)

288-317: Avoid shadowing scim2RestClient field and ensure the org-scoped client is closed

Inside testInvokeUserMgtAPIsFromOrganizationAppToken you declare:

SCIM2RestClient scim2RestClient = new SCIM2RestClient(serverURL, tenantInfo);

This shadows the class field scim2RestClient and is never closed, which:

  • Makes the code harder to read (two different scim2RestClients in the same class).
  • Leaks the underlying HTTP client for the org-scoped instance.

Consider renaming and closing it:

-        // Creating the URLs with the tenant details provided.
-        SCIM2RestClient scim2RestClient = new SCIM2RestClient(serverURL, tenantInfo);
+        // Creating the URLs with the tenant details provided.
+        SCIM2RestClient orgScim2RestClient = new SCIM2RestClient(serverURL, tenantInfo);
@@
-        String subOrgUserId = scim2RestClient.createSubOrgUser(endUser, orgAppAccessToken);
+        String subOrgUserId = orgScim2RestClient.createSubOrgUser(endUser, orgAppAccessToken);
@@
-        org.json.simple.JSONObject subOrgUserObject = scim2RestClient.getSubOrgUser(subOrgUserId, null,
-                orgAppAccessToken);
+        org.json.simple.JSONObject subOrgUserObject = orgScim2RestClient.getSubOrgUser(subOrgUserId, null,
+                orgAppAccessToken);
@@
-        assertTrue(subOrgUserObject.get("emails").toString().contains(TEST_ORG_END_USER_EMAIL), "Expected " +
-                "user emails are not present in the response.");
+        assertTrue(subOrgUserObject.get("emails").toString().contains(TEST_ORG_END_USER_EMAIL), "Expected " +
+                "user emails are not present in the response.");
+        orgScim2RestClient.closeHttpClient();

(Or use a try/finally if you want to be defensive against assertion failures.)


1-1: Add standard license header to the new test class

This new test class is missing the usual WSO2 Apache 2.0 license header that appears in other test classes in this package. Please add the standard header for consistency and compliance.


165-172: Harden teardown: close all clients and guard against partial initialization

atEnd() currently:

  • Deletes the organization but does not close orgMgtRestClient.
  • Assumes all fields (organizationId, accessToken, clients) are non-null and will succeed; an exception in one operation will skip the rest.

Suggested refactor:

  • Close orgMgtRestClient as well.
  • Wrap each cleanup step in its own try-catch with null checks so one failure doesn’t prevent others.
    @AfterClass(alwaysRun = true)
    public void atEnd() throws Exception {

-        orgMgtRestClient.deleteOrganizationWithToken(organizationId, accessToken);
-        restClient.closeHttpClient();
-        scim2RestClient.closeHttpClient();
-        client.close();
+        try {
+            if (organizationId != null && accessToken != null && orgMgtRestClient != null) {
+                orgMgtRestClient.deleteOrganizationWithToken(organizationId, accessToken);
+            }
+        } catch (Exception e) {
+            // Log if you have a logger; keep going with other cleanups.
+        }
+        try {
+            if (orgMgtRestClient != null) {
+                orgMgtRestClient.closeHttpClient();
+            }
+        } catch (Exception ignored) { }
+        try {
+            if (restClient != null) {
+                restClient.closeHttpClient();
+            }
+        } catch (Exception ignored) { }
+        try {
+            if (scim2RestClient != null) {
+                scim2RestClient.closeHttpClient();
+            }
+        } catch (Exception ignored) { }
+        try {
+            if (client != null) {
+                client.close();
+            }
+        } catch (Exception ignored) { }
    }

384-418: Make authorized-API updates more robust and fix assertion message

In updateAuthorizedAPIs:

  1. Assumes at least one API resource exists

If the filter doesn’t match any API resource, this line will throw:

apiUUID = apiResourceResponseBody.getJSONArray(API_RESOURCES).getJSONObject(0).getString(ID);

Consider asserting that the array is non-empty so failures are explicit:

-                JSONObject apiResourceResponseBody =
-                        new JSONObject(EntityUtils.toString(apiResourceResponse.getEntity()));
-                apiUUID = apiResourceResponseBody.getJSONArray(API_RESOURCES).getJSONObject(0).getString(ID);
+                JSONObject apiResourceResponseBody =
+                        new JSONObject(EntityUtils.toString(apiResourceResponse.getEntity()));
+                if (apiResourceResponseBody.getJSONArray(API_RESOURCES).isEmpty()) {
+                    throw new AssertionError("No API resource found for identifier: " + apiName);
+                }
+                apiUUID = apiResourceResponseBody.getJSONArray(API_RESOURCES).getJSONObject(0).getString(ID);

(Adjust for the exact JSON API; some versions expose length() instead of isEmpty().)

  1. Assertion message logs the response object, not app id

The failure message currently interpolates appAuthorizedAPIsResponse instead of the application id:

String.format("Authorized APIs update failed for application with ID: %s for API: %s.",
        appAuthorizedAPIsResponse, apiName)

This produces an unhelpful string like CloseableHttpResponse@.... Use appId (and, optionally, the response body) instead:

-                assertEquals(appAuthorizedAPIsResponse.getStatusLine().getStatusCode(), HttpStatus.SC_OK,
-                        String.format("Authorized APIs update failed for application with ID: %s for API: %s.",
-                                appAuthorizedAPIsResponse, apiName));
+                String responseBody = EntityUtils.toString(appAuthorizedAPIsResponse.getEntity());
+                assertEquals(appAuthorizedAPIsResponse.getStatusLine().getStatusCode(), HttpStatus.SC_OK,
+                        String.format("Authorized APIs update failed for application with ID: %s for API: %s. " +
+                                      "Response body: %s", appId, apiName, responseBody));

The same two concerns apply to updateOrganizationAppAuthorizedAPIs; it would be good to align both helpers.

modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java (2)

272-332: Fix applicationConfig null handling and audience condition in addOrganizationApplication

Two issues here:

  1. Null applicationConfig path creates a root app, not an org app

When applicationConfig is null, the method delegates to getBasicOAuthApplication(CALLBACK_URL), which internally creates a root application via addApplication(application), ignoring both applicationName and switchedAccessToken. That contradicts the method contract (“Add an application to an organization”) and will never produce an org-scoped app in that path.

If you intend to support a “default config” branch, it should still go through restClient.createOrganizationApplication(...), for example:

-        if (applicationConfig == null) {
-            return getBasicOAuthApplication(CALLBACK_URL);
-        }
+        if (applicationConfig == null) {
+            ApplicationModel defaultApp = new ApplicationModel();
+            defaultApp.setName(applicationName);
+
+            OpenIDConnectConfiguration oidcConfig = new OpenIDConnectConfiguration();
+            oidcConfig.setGrantTypes(Collections.singletonList("authorization_code"));
+            oidcConfig.setCallbackURLs(Collections.singletonList(CALLBACK_URL));
+
+            InboundProtocols inboundProtocolsConfig = new InboundProtocols();
+            inboundProtocolsConfig.setOidc(oidcConfig);
+            defaultApp.setInboundProtocolConfiguration(inboundProtocolsConfig);
+
+            String appId = restClient.createOrganizationApplication(defaultApp, switchedAccessToken);
+            return restClient.getOrganizationApplication(appId, switchedAccessToken);
+        }

(Adjust defaults to whatever is appropriate for your org apps.)

  1. Audience configuration is gated on requested-claims emptiness

The condition

if (applicationConfig.getAudienceList() != null && !applicationConfig.getRequestedClaimList().isEmpty()) {
    oidcConfig.idToken(new IdTokenConfiguration().audience(applicationConfig.getAudienceList()));
}

ties audience configuration to requested claims rather than the actual audience list. If requestedClaimList is empty but audienceList is populated, audiences will not be applied.

You likely want the same fix that was suggested earlier for the non-org addApplication path—check the audience list itself:

-        if (applicationConfig.getAudienceList() != null && !applicationConfig.getRequestedClaimList().isEmpty()) {
+        if (applicationConfig.getAudienceList() != null && !applicationConfig.getAudienceList().isEmpty()) {
             oidcConfig.idToken(new IdTokenConfiguration().audience(applicationConfig.getAudienceList()));
         }

Consider applying the same audience-condition fix to the existing addApplication(ApplicationConfig) method for consistency.


486-499: Tidy OIDC org-inbound helper Javadoc and call formatting

The logic of getOIDCInboundDetailsOfOrganizationApplication is fine, but there are two small nits:

  • Javadoc parameter text: “Authorized token to get the OIDC inbound configurations or an application” should be “of an application”.
  • Method call has a double space between appId, and switchedAccessToken.

Suggested cleanup:

-     * @param switchedAccessToken Authorized token to get the OIDC inbound configurations or an application.
+     * @param switchedAccessToken Authorized token to get the OIDC inbound configurations of an application.
@@
-        return restClient.getOIDCInboundDetailsOfOrganizationApp(appId,  switchedAccessToken);
+        return restClient.getOIDCInboundDetailsOfOrganizationApp(appId, switchedAccessToken);
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java (2)

211-227: Add error handling and Location-header checks in createOrganizationV2Roles

createOrganizationV2Roles still assumes success and directly indexes the Location header:

String[] locationElements = response.getHeaders(LOCATION_HEADER)[0].toString().split(PATH_SEPARATOR);
return locationElements[locationElements.length - 1];

If the role creation fails (4xx/5xx) or the header is missing, this will throw an ArrayIndexOutOfBoundsException or NullPointerException and obscure the real server error.

Align this method with the robustness of createOrganizationApplication by validating the response and the header:

    public String createOrganizationV2Roles(RoleV2 role, String accessToken) throws IOException {

        String jsonRequest = toJSONString(role);
        try (CloseableHttpResponse response = getResponseOfHttpPost(organizationRoleV2ApiBasePath, jsonRequest,
                getHeadersWithBearerToken(accessToken))) {
-            String[] locationElements = response.getHeaders(LOCATION_HEADER)[0].toString().split(PATH_SEPARATOR);
-            return locationElements[locationElements.length - 1];
+            int status = response.getStatusLine().getStatusCode();
+            if (status < 200 || status >= 300) {
+                String responseBody = EntityUtils.toString(response.getEntity());
+                throw new IOException("Error occurred while creating the role. Status: " + status +
+                        ", Response: " + responseBody);
+            }
+            Header[] locationHeaders = response.getHeaders(LOCATION_HEADER);
+            if (locationHeaders == null || locationHeaders.length == 0) {
+                throw new IOException("Location header missing in role creation response");
+            }
+            String[] locationElements = locationHeaders[0].toString().split(PATH_SEPARATOR);
+            return locationElements[locationElements.length - 1];
        }
    }

490-505: Clarify naming of getConfigOrOrganizationApp and reuse pattern

The org-scoped OIDC helper:

public OpenIDConnectConfiguration getOIDCInboundDetailsOfOrganizationApp(String appId, String accessToken)

delegates to getConfigOrOrganizationApp, whose name is confusing (“Or” instead of “Of”) and doesn’t convey that it’s org-specific inbound-config retrieval:

private String getConfigOrOrganizationApp(String appId, String inboundType, String accessToken)

The implementation itself is fine, but for readability and consistency with getConfig(...) you may want to:

  • Rename the helper to something like getOrgInboundConfig and fix the “Or/Of” typo.
  • Narrow its responsibility to “org inbound config” (which is how you currently use it) rather than a vague “config or organization app”.

Example:

-    private String getConfigOrOrganizationApp(String appId, String inboundType,
-                                              String accessToken) throws Exception {
+    private String getOrgInboundConfig(String appId, String inboundType, String accessToken) throws Exception {
@@
-        String responseBody = getConfigOrOrganizationApp(appId, OIDC, accessToken);
+        String responseBody = getOrgInboundConfig(appId, OIDC, accessToken);

This keeps the org/non-org variants parallel (getConfig vs getOrgInboundConfig) and makes future maintenance easier.

🧹 Nitpick comments (1)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2AuthorizationCodeGrantForSubOrgAppsTestCase.java (1)

264-286: Close the temporary OAuth2RestClient to avoid leaking HTTP connections

testInvokeApplicationMgtAPIsFromOrganizationAppToken creates a new OAuth2RestClient instance but never closes its underlying HTTP client:

OAuth2RestClient oAuth2RestClient = new OAuth2RestClient(serverURL, tenantInfo);

Over many tests this can accumulate open connections. Since the test method already throws Exception, you can safely close it in a finally block:

-        OAuth2RestClient oAuth2RestClient = new OAuth2RestClient(serverURL, tenantInfo);
-
-        // Validate application creation
-        ApplicationModel applicationModel = getOrganizationAppModel(ORGANIZATION_TEST_APP_NAME);
-        String orgAppId = oAuth2RestClient.createOrganizationApplication(applicationModel, orgAppAccessToken);
-        ...
+        OAuth2RestClient oAuth2RestClient = new OAuth2RestClient(serverURL, tenantInfo);
+        try {
+            // Validate application creation
+            ApplicationModel applicationModel = getOrganizationAppModel(ORGANIZATION_TEST_APP_NAME);
+            String orgAppId = oAuth2RestClient.createOrganizationApplication(applicationModel, orgAppAccessToken);
+            ...
+        } finally {
+            oAuth2RestClient.closeHttpClient();
+        }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 666cb80 and c176ccd.

📒 Files selected for processing (9)
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2AuthorizationCodeGrantForSubOrgAppsTestCase.java (1 hunks)
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java (3 hunks)
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java (8 hunks)
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OrgMgtRestClient.java (2 hunks)
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/SCIM2RestClient.java (1 hunks)
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java (1 hunks)
  • modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/oauth2/management-app-authorized-apis.json (1 hunks)
  • modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/oauth2/organization-app-authorized-apis.json (1 hunks)
  • modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/oauth2/organization-app-authorized-apis.json
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OrgMgtRestClient.java
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java
🧰 Additional context used
🧬 Code graph analysis (1)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java (1)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java (1)
  • OAuth2Constant (24-150)
🔇 Additional comments (2)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/SCIM2RestClient.java (1)

522-541: Org-scoped role update helper is consistent with existing patterns

The updateOrganizationUserRole implementation correctly mirrors existing SCIM role update helpers (path, headers, and 200 assertion) and cleanly encapsulates the org-scoped PATCH call. No changes needed.

modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml (1)

157-157: New OAuth2 sub-org test is correctly wired into TestNG suite

Including OAuth2AuthorizationCodeGrantForSubOrgAppsTestCase in the default configuration test group is consistent with other OAuth2 tests and preserves existing suite settings.

@ShanChathusanda93 ShanChathusanda93 force-pushed the sub-org-tenant-app-branch branch from c176ccd to 7f8f3ce Compare December 5, 2025 04:30
@ShanChathusanda93 ShanChathusanda93 force-pushed the sub-org-tenant-app-branch branch from 7f8f3ce to 33f70e5 Compare December 21, 2025 04:17
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java (1)

272-332: Clarify behavior of addOrganizationApplication when applicationConfig is null

The null-config branch calls getBasicOAuthApplication(CALLBACK_URL), which creates a root-level application rather than an organization-scoped one, inconsistent with the method’s name and Javadoc. Either forbid null here (and fail fast) or introduce an organization-scoped “basic” app path so the helper always returns an org application.

Example: make config mandatory
-    public ApplicationResponseModel addOrganizationApplication(String applicationName,
-                                                               ApplicationConfig applicationConfig,
-                                                               String switchedAccessToken) throws Exception {
-
-        if (applicationConfig == null) {
-            return getBasicOAuthApplication(CALLBACK_URL);
-        }
+    public ApplicationResponseModel addOrganizationApplication(String applicationName,
+                                                               ApplicationConfig applicationConfig,
+                                                               String switchedAccessToken) throws Exception {
+
+        if (applicationConfig == null) {
+            throw new IllegalArgumentException("applicationConfig must not be null for organization applications");
+        }
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2AuthorizationCodeGrantForSubOrgAppsTestCase.java (1)

525-534: Consider avoiding reliance on the mutable switchedM2MToken field in helpers

getHeadersWithToken and updateOrganizationAppAuthorizedAPIs implicitly depend on the class-level switchedM2MToken, which couples these helpers to prior switchM2MAccessToken calls. Passing the token in as a parameter (and threading it through) would make these utilities more self-contained and less order-dependent.

Illustrative refactor
-    private Header[] getHeadersWithToken() {
+    private Header[] getHeadersWithToken(String token) {
@@
-        headerList[1] = new BasicHeader(AUTHORIZATION_ATTRIBUTE, BEARER_TOKEN_AUTHORIZATION_ATTRIBUTE
-                + switchedM2MToken);
+        headerList[1] = new BasicHeader(AUTHORIZATION_ATTRIBUTE, BEARER_TOKEN_AUTHORIZATION_ATTRIBUTE
+                + token);
@@
-                    getHeadersWithToken())) {
+                    getHeadersWithToken(switchedM2MToken))) {
@@
-                    authorizedAPIRequestBody.toString(), getHeadersWithToken())) {
+                    authorizedAPIRequestBody.toString(), getHeadersWithToken(switchedM2MToken))) {

Also applies to: 576-599

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7f8f3ce and 33f70e5.

📒 Files selected for processing (9)
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2AuthorizationCodeGrantForSubOrgAppsTestCase.java (1 hunks)
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java (3 hunks)
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java (8 hunks)
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OrgMgtRestClient.java (2 hunks)
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/SCIM2RestClient.java (1 hunks)
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java (1 hunks)
  • modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/oauth2/management-app-authorized-apis.json (1 hunks)
  • modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/oauth2/organization-app-authorized-apis.json (1 hunks)
  • modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java
  • modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml
  • modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/oauth2/management-app-authorized-apis.json
  • modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/oauth2/organization-app-authorized-apis.json
🧰 Additional context used
🧬 Code graph analysis (2)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java (1)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java (1)
  • OAuth2Constant (24-150)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2AuthorizationCodeGrantForSubOrgAppsTestCase.java (6)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/dataprovider/model/ApplicationConfig.java (1)
  • ApplicationConfig (23-152)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/rest/api/common/RESTTestBase.java (1)
  • RESTTestBase (76-1060)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java (1)
  • OAuth2RestClient (102-1403)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OrgMgtRestClient.java (1)
  • OrgMgtRestClient (69-527)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/SCIM2RestClient.java (1)
  • SCIM2RestClient (44-1106)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java (1)
  • OAuth2Constant (24-150)
🔇 Additional comments (12)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/SCIM2RestClient.java (1)

522-541: Organization role update helper is consistent with existing SCIM2 patterns

The new updateOrganizationUserRole correctly targets the sub-organization Roles V2 path, uses the provided bearer token, and asserts 200 OK, matching the existing role/user update helpers.

modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OrgMgtRestClient.java (2)

137-155: Token-based organization creation helper looks correct

addOrganizationWithToken cleanly reuses buildOrgCreationRequestBody and the existing root org path while swapping to bearer-token auth, which aligns with the intended M2M/token-based flows.


210-225: Token-based organization deletion helper is aligned with existing delete flow

deleteOrganizationWithToken mirrors deleteOrganization but externalizes the token, preserving path, expectations (204 NO_CONTENT), and assertion behavior.

modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java (2)

432-443: Organization role wrapper is a thin, appropriate delegation

addOrganizationRole is a simple façade over restClient.createOrganizationV2Roles, keeping test code decoupled from the REST client details.


486-499: Organization OIDC inbound retrieval mirrors existing root-tenant helper

getOIDCInboundDetailsOfOrganizationApplication uses the same pattern as getOIDCInboundDetails, delegating to the new org-scoped REST client method and deserializing into OpenIDConnectConfiguration; the Javadoc and parameterization are consistent.

modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2AuthorizationCodeGrantForSubOrgAppsTestCase.java (4)

153-201: End-to-end sub-organization setup sequence is coherent

The @BeforeClass flow (create and share management app, obtain M2M token, create org, switch token, create org app, user, and role) is internally consistent and uses the new org-scoped clients as intended. Teardown also now deletes the organization and closes all clients.


269-301: Access-token acquisition and scope assertions exercise the right behavior

The auth-code token request for the organization app correctly uses the tenant-qualified token endpoint and basic auth, and the assertions on internal_org_user_mgt_list and internal_org_application_mgt_create ensure that organization-scoped permissions are actually present in the issued token.


390-422: Organization application creation helper correctly wires org-scoped flows

createOrganizationApplication builds an ApplicationConfig with the expected grant types (CC, RO, auth code), delegates to addOrganizationApplication with the org token, then sets up authorized APIs and reads back OIDC client credentials from the organization context; this matches the intended org-scoped app lifecycle.


424-495: Org and root authorized-API updaters are consistent with existing patterns

Both updateAuthorizedAPIs (root) and updateOrganizationAppAuthorizedAPIs (org) correctly resolve API resources by identifier, construct RBAC-based authorized-API payloads, and assert 200 OK on update using appropriate headers (basic vs. bearer) and paths (plain vs. o/).

modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java (3)

121-144: Organization-scoped application creation wiring looks correct

The new organizationRoleV2ApiBasePath and createOrganizationApplication correctly reuse the sub-org application base path with bearer authorization, validate non-2xx responses, and parse the application ID from the Location header in the same way as the root-scoped createApplication.

Also applies to: 170-192


211-231: Org V2 role creation and SCIM2 org-role path helper are aligned

createOrganizationV2Roles targets organizationRoleV2ApiBasePath, uses bearer auth, adds basic error reporting for 4xx/5xx, and extracts the ID from the Location header. The new getOrganizationSCIM2RoleV2Path composes the expected /o/scim2/v2/Roles (or tenant-qualified) URL, matching the established org-path pattern.

Also applies to: 895-902


283-303: Organization application and OIDC inbound getters mirror root-tenant behavior

getOrganizationApplication and getOIDCInboundDetailsOfOrganizationApp reuse the same deserialization approach as their root equivalents, but switch to sub-org application paths and bearer headers. getConfigOfOrganizationApp is a small, focused helper that keeps inbound-config URL construction and HTTP details encapsulated.

Also applies to: 494-508, 535-545

Comment on lines +646 to +868
private ApplicationModel getOrganizationAppModel(String applicationName) {

ApplicationModel application = new ApplicationModel();
application.setName(applicationName);

OpenIDConnectConfiguration oidcConfig = new OpenIDConnectConfiguration();
oidcConfig.setGrantTypes(Arrays.asList(OAuth2Constant.OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE,
OAuth2Constant.OAUTH2_GRANT_TYPE_RESOURCE_OWNER,
OAuth2Constant.OAUTH2_GRANT_TYPE_CLIENT_CREDENTIALS));
oidcConfig.setCallbackURLs(Collections.singletonList(OAuth2Constant.CALLBACK_URL));

AccessTokenConfiguration accessTokenConfiguration = new AccessTokenConfiguration();
accessTokenConfiguration.type(ApplicationConfig.TokenType.OPAQUE.getTokenTypeProperty());
accessTokenConfiguration.applicationAccessTokenExpiryInSeconds(86400L);
accessTokenConfiguration.userAccessTokenExpiryInSeconds(300L);

oidcConfig.idToken(new IdTokenConfiguration().audience(Arrays.asList("audience01", "audience02")));

InboundProtocols inboundProtocolsConfig = new InboundProtocols();
inboundProtocolsConfig.setOidc(oidcConfig);
application.setInboundProtocolConfiguration(inboundProtocolsConfig);

application.advancedConfigurations(new AdvancedApplicationConfiguration()
.skipLoginConsent(true)
.skipLogoutConsent(true));

return application;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Access token configuration in getOrganizationAppModel is built but never applied

getOrganizationAppModel constructs an AccessTokenConfiguration (type and expiry values) but never attaches it to oidcConfig, so those settings are effectively ignored and default server behavior is used instead.

Proposed fix: attach the access token configuration
         AccessTokenConfiguration accessTokenConfiguration = new AccessTokenConfiguration();
         accessTokenConfiguration.type(ApplicationConfig.TokenType.OPAQUE.getTokenTypeProperty());
         accessTokenConfiguration.applicationAccessTokenExpiryInSeconds(86400L);
         accessTokenConfiguration.userAccessTokenExpiryInSeconds(300L);
 
-        oidcConfig.idToken(new IdTokenConfiguration().audience(Arrays.asList("audience01", "audience02")));
+        oidcConfig.accessToken(accessTokenConfiguration);
+        oidcConfig.idToken(new IdTokenConfiguration().audience(Arrays.asList("audience01", "audience02")));
🤖 Prompt for AI Agents
In
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2AuthorizationCodeGrantForSubOrgAppsTestCase.java
around lines 646 to 673, the AccessTokenConfiguration instance is created but
never attached to the OpenIDConnectConfiguration, so its type and expiry
settings are ignored; fix this by calling the appropriate setter on oidcConfig
to attach the accessTokenConfiguration (for example,
oidcConfig.setAccessToken(accessTokenConfiguration) or
oidcConfig.accessToken(accessTokenConfiguration) depending on the API) before
assigning oidcConfig to inboundProtocolsConfig.

@ShanChathusanda93 ShanChathusanda93 force-pushed the sub-org-tenant-app-branch branch from 33f70e5 to cf08079 Compare December 23, 2025 05:36
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2AuthorizationForSubOrganizationAppsTestCase.java (1)

837-864: AccessTokenConfiguration is built but never attached to oidcConfig.

The AccessTokenConfiguration is created with type and expiry settings but is never applied to oidcConfig, so these settings are ignored and server defaults are used instead.

🔎 Proposed fix (from previous review)
         AccessTokenConfiguration accessTokenConfiguration = new AccessTokenConfiguration();
         accessTokenConfiguration.type(ApplicationConfig.TokenType.OPAQUE.getTokenTypeProperty());
         accessTokenConfiguration.applicationAccessTokenExpiryInSeconds(86400L);
         accessTokenConfiguration.userAccessTokenExpiryInSeconds(300L);
 
+        oidcConfig.accessToken(accessTokenConfiguration);
         oidcConfig.idToken(new IdTokenConfiguration().audience(Arrays.asList("audience01", "audience02")));
🧹 Nitpick comments (4)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2AuthorizationForSubOrganizationAppsTestCase.java (2)

603-636: Resource leak: RestBaseClient instance not closed.

The restBaseClient instance created here is never explicitly closed. While it's used only within this method, the underlying HTTP client may hold connections.

🔎 Proposed fix

Consider using try-with-resources or explicitly closing the client. Since RestBaseClient doesn't implement AutoCloseable, you could call closeHttpClient() after use, or refactor to reuse an existing client instance:

 private void updateAuthorizedAPIs(String appId, JSONObject authorizedAPIs) throws JSONException, IOException {
 
     RestBaseClient restBaseClient = new RestBaseClient();
+    try {
         String apiResourceManagementApiBasePath = buildPath(serverURL, tenantInfo.getDomain(), false,
                 API_RESOURCE_MANAGEMENT_PATH);
         // ... rest of the method
+    } finally {
+        restBaseClient.closeHttpClient();
+    }
 }

639-673: Same resource leak concern as updateAuthorizedAPIs.

The restBaseClient instance should be closed after use to prevent connection leaks.

modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java (2)

259-279: Consider adding error handling for non-success responses.

If the GET request fails (4xx/5xx), the response body may not be valid JSON, causing a confusing JsonParseException. Adding status validation would provide clearer error messages.

🔎 Suggested improvement
     public ApplicationResponseModel getOrganizationApplication(String appId, String accessToken)
             throws IOException {
 
         String endPointUrl = subOrgApplicationManagementApiBasePath + PATH_SEPARATOR + appId;
 
         try (CloseableHttpResponse response = getResponseOfHttpGet(endPointUrl,
                 getHeadersWithBearerToken(accessToken))) {
+            int statusCode = response.getStatusLine().getStatusCode();
+            if (statusCode >= 400) {
+                String responseBody = EntityUtils.toString(response.getEntity());
+                throw new RuntimeException("Error getting organization application. Status: " + statusCode +
+                        ", Response: " + responseBody);
+            }
             String responseBody = EntityUtils.toString(response.getEntity());
 
             ObjectMapper jsonWriter = new ObjectMapper(new JsonFactory());
             return jsonWriter.readValue(responseBody, ApplicationResponseModel.class);
         }
     }

511-521: Private helper lacks error handling.

Similar to other GET methods, this helper returns the raw response body without validating the HTTP status code. If the request fails, calling code will receive an error response body and JSON parsing may fail with an unclear error.

Since this is a private helper method, the impact is contained, but for consistency with the pattern used in createOrganizationApplication, consider validating the response status.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 33f70e5 and cf08079.

📒 Files selected for processing (9)
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2AuthorizationForSubOrganizationAppsTestCase.java
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OrgMgtRestClient.java
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/SCIM2RestClient.java
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java
  • modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/oauth2/management-app-authorized-apis.json
  • modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/oauth2/organization-app-authorized-apis.json
  • modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml
🚧 Files skipped from review as they are similar to previous changes (6)
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java
  • modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OrgMgtRestClient.java
  • modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/oauth2/management-app-authorized-apis.json
  • modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/oauth2/organization-app-authorized-apis.json
🔇 Additional comments (11)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/SCIM2RestClient.java (2)

522-541: LGTM!

The method correctly updates an organization role using PATCH with bearer token authentication. It follows the same pattern as other role update methods in this class.


695-715: LGTM!

Good implementation with proper error handling. The method correctly validates the 201 status code before accessing the Location header, preventing ArrayIndexOutOfBoundsException on failure. The error response body is included in the exception message for debugging.

modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2AuthorizationForSubOrganizationAppsTestCase.java (7)

1-17: LGTM - License header is present.

The copyright header is correctly included.


177-191: LGTM - Test coverage for both tenant modes.

The data provider correctly includes both SUPER_TENANT_ADMIN and TENANT_ADMIN modes, ensuring the test validates flows for both super tenant and tenant organizations.


211-268: Well-structured test initialization.

The test initialization properly:

  • Creates required REST clients
  • Sets up M2M token and organization context
  • Creates test resources (API resource, application, user, role)

The test dependency chain is clear.


343-362: Good coverage for JWT token type flow.

The test correctly updates the application token type to JWT and validates the configuration change before proceeding with the JWT flow. Clearing sessions before the JWT flow ensures a clean test state.


438-492: Comprehensive JWT token structure validation.

The tests validate:

  • JWT has three parts (header.payload.signature)
  • Standard claims are present (sub, iat, exp, client_id)
  • client_id matches the expected application

This provides good coverage for JWT token format verification.


494-530: LGTM - Proper cleanup with null checks.

The cleanup methods properly check for null before closing resources and deleting test data. The AfterSuite correctly restores server configuration.


903-915: PUT is the correct HTTP method for this OIDC configuration update endpoint.

The WSO2 application management API uses PUT for the /inbound-protocols/oidc endpoint, as confirmed by the API implementation. The code is correct as written.

modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java (2)

168-190: LGTM - Good error handling for organization application creation.

The method correctly validates the 201 status code before accessing the Location header, and includes the response body in the exception for debugging failures.


470-484: LGTM - Properly delegates to helper method.

The method correctly delegates to getConfigOfOrganizationApp and deserializes the response to OpenIDConnectConfiguration.

@ShanChathusanda93 ShanChathusanda93 force-pushed the sub-org-tenant-app-branch branch from cf08079 to 1ecfbb9 Compare December 23, 2025 06:09
@ShanChathusanda93 ShanChathusanda93 force-pushed the sub-org-tenant-app-branch branch from 1ecfbb9 to 3e57639 Compare December 23, 2025 06:22
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2AuthorizationForSubOrganizationAppsTestCase.java (2)

841-868: AccessTokenConfiguration in getOrganizationAppModel is built but never applied

You construct AccessTokenConfiguration (type + expiry) but never attach it to oidcConfig, so those settings are ignored and server defaults are used. This mirrors a previously flagged pattern in an earlier sub-org OAuth2 test.

Consider wiring it like you do elsewhere:

Suggested fix
        AccessTokenConfiguration accessTokenConfiguration = new AccessTokenConfiguration();
        accessTokenConfiguration.type(ApplicationConfig.TokenType.OPAQUE.getTokenTypeProperty());
        accessTokenConfiguration.applicationAccessTokenExpiryInSeconds(86400L);
        accessTokenConfiguration.userAccessTokenExpiryInSeconds(300L);

-        oidcConfig.idToken(new IdTokenConfiguration().audience(Arrays.asList("audience01", "audience02")));
+        oidcConfig.accessToken(accessTokenConfiguration);
+        oidcConfig.idToken(new IdTokenConfiguration().audience(Arrays.asList("audience01", "audience02")));

892-920: Use the accessToken parameter consistently instead of the switchedM2MToken field

updateOrganizationApplicationTokenType accepts an accessToken parameter and uses it when fetching the current OIDC config, but the subsequent PUT reuses the switchedM2MToken field via getHeadersWithToken(). This couples the method to external state and makes the parameter slightly misleading.

To keep it self-contained and future-proof, consider:

  • Using the method parameter for both GET and PUT, or
  • Passing the token explicitly into getHeadersWithToken/a new helper that accepts a token.

This mirrors an earlier concern you already fixed in another sub-org OAuth2 test.

🧹 Nitpick comments (6)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java (1)

31-31: Use the new org-switch grant constant at call sites

The new OAUTH2_GRANT_TYPE_ORGANIZATION_SWITCH = "organization_switch" constant looks correct. Consider replacing hard-coded "organization_switch" usages (e.g., in OrgMgtRestClient.switchM2MToken and OAuth2AuthorizationForSubOrganizationAppsTestCase.switchM2MAccessToken) with this constant for consistency and easier maintenance.

modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/SCIM2RestClient.java (1)

695-715: Org-scoped V2 role creation is fine; optionally guard for missing Location header

The flow (POST to sub-org v2 roles, expect 201, parse role ID from Location) is correct and consistent with other clients. For extra robustness, you could defensively check that response.getHeaders(LOCATION_HEADER) is non-empty before indexing [0], and throw a clearer error if the header is missing.

modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OrgMgtRestClient.java (2)

137-154: Token-based organization creation mirrors existing flow but lacks status validation

addOrganizationWithToken reuses buildOrgCreationRequestBody and the same URL as addOrganization, which is good. However, like the original method, it assumes success and immediately indexes LOCATION_HEADER[0]. Consider checking the status code (e.g., 201) and throwing with the response body on failure so tests get a clear error instead of an ArrayIndexOutOfBoundsException if the call fails.


271-296: Prefer using the org-switch grant constant in switchM2MToken

In switchM2MToken, the grant type is still passed as the literal "organization_switch". Now that OAuth2Constant.OAUTH2_GRANT_TYPE_ORGANIZATION_SWITCH exists, consider using it instead:

Suggested change
-        urlParameters.add(new BasicNameValuePair(OAuth2Constant.GRANT_TYPE_NAME, "organization_switch"));
+        urlParameters.add(new BasicNameValuePair(
+                OAuth2Constant.GRANT_TYPE_NAME,
+                OAuth2Constant.OAUTH2_GRANT_TYPE_ORGANIZATION_SWITCH));
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java (1)

272-332: Org-scoped application creation mirrors the non-org path correctly

addOrganizationApplication(...) essentially clones the addApplication(ApplicationConfig) logic but:

  • Uses the provided applicationName instead of a fixed name.
  • Delegates to restClient.createOrganizationApplication and getOrganizationApplication with the org token.

This is the right abstraction for org apps. One minor point: the applicationConfig == null branch calls getBasicOAuthApplication(CALLBACK_URL), which creates a non-org app; if you ever rely on that branch, you may want an org-specific basic helper instead.

modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java (1)

259-279: Org-scoped getApplication helper is fine; consider asserting success status

getOrganizationApplication is the org variant of getApplication, using the sub-org base path and bearer headers, then mapping the JSON into ApplicationResponseModel. That’s correct.

Optionally, you could assert an expected 2xx status before parsing to make failures more obvious during test debugging.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1ecfbb9 and 3e57639.

📒 Files selected for processing (9)
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2AuthorizationForSubOrganizationAppsTestCase.java
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OrgMgtRestClient.java
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/SCIM2RestClient.java
  • modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java
  • modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/oauth2/management-app-authorized-apis.json
  • modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/oauth2/organization-app-authorized-apis.json
  • modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml
🚧 Files skipped from review as they are similar to previous changes (2)
  • modules/integration/tests-integration/tests-backend/src/test/resources/testng.xml
  • modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/oauth2/organization-app-authorized-apis.json
🧰 Additional context used
🧬 Code graph analysis (1)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java (1)
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/utils/OAuth2Constant.java (1)
  • OAuth2Constant (24-150)
🔇 Additional comments (9)
modules/integration/tests-integration/tests-backend/src/test/resources/org/wso2/identity/integration/test/oauth2/management-app-authorized-apis.json (1)

1-35: Authorized-APIs mapping looks consistent with org endpoints

The endpoint-to-permission mappings align with the org-scoped APIs used in the tests and with the internal_org_* permissions referenced elsewhere. No issues from a test/resources perspective.

modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/SCIM2RestClient.java (1)

522-541: Org-scoped role PATCH helper is consistent with existing patterns

updateOrganizationUserRole correctly:

  • Uses the sub-org v2 roles path.
  • Sends the bearer token via getHeadersWithBearerToken.
  • Asserts SC_OK on success.

This mirrors the existing user/role update helpers and looks sound.

modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OrgMgtRestClient.java (1)

210-224: Explicit-token delete helper is consistent with existing deleteOrganization

deleteOrganizationWithToken cleanly mirrors deleteOrganization, differing only in how the token is obtained. The assertion on SC_NO_CONTENT is appropriate; no functional issues here.

modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2AuthorizationForSubOrganizationAppsTestCase.java (3)

181-195: Data provider + factory setup correctly exercises both super-tenant and tenant modes

Using @DataProvider + @Factory to run the same flow for SUPER_TENANT_ADMIN and TENANT_ADMIN with distinct org handles (sub001, sub002) is a good pattern and aligns with earlier review feedback to cover both modes.


536-565: Management app bootstrap flow is solid and reuses existing abstractions

addAndShareApp() cleanly composes ApplicationConfig, creates the management app via addApplication, wires authorized APIs from management-app-authorized-apis.json, fetches OIDC inbound details, and shares the app. This nicely encapsulates the management-app setup used later in the tests.


368-397: JWT/OPAQUE token-cycle coverage and introspection assertions are thorough

The second half of the test suite (updating token type to JWT, re-running the auth code flow, validating JWT structure and introspection fields, and comparing token characteristics) provides strong coverage for both opaque and JWT tokens on sub-org apps, including the introspection behavior difference. This is valuable regression coverage for the new org-scoped flows.

modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2ServiceAbstractIntegrationTest.java (1)

473-486: Org-specific OIDC inbound retrieval helper is straightforward and consistent

getOIDCInboundDetailsOfOrganizationApplication is a thin, clear delegate to restClient.getOIDCInboundDetailsOfOrganizationApp, and the Javadoc correctly documents the token parameter as the auth mechanism. No issues here.

modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java (2)

168-190: Org-scoped application creation reuses existing patterns and handles errors well

createOrganizationApplication:

  • Serializes the ApplicationModel as JSON.
  • POSTs to the sub-org applications base path with a bearer token.
  • Treats only 201 as success and surfaces the error body otherwise.

This is a good, explicit pattern for test clients.


470-485: Org-scoped OIDC config retrieval correctly mirrors the root-tenant helper

getOIDCInboundDetailsOfOrganizationApp + getConfigOfOrganizationApp are the org-scoped counterparts to getOIDCInboundDetails / getConfig. They use the same URL shape with the sub-org applications base path and bearer token. Implementation looks correct and consistent.

Comment on lines +1059 to +1082
private OAuth2RestClient createOAuth2RestClientForOrganization() throws IOException {

Tenant tenantInfo = new Tenant();
tenantInfo.setDomain(ORGANIZATION_HANDLE);
User user = new User();
user.setUserName("testadmin@" + ORGANIZATION_HANDLE);
tenantInfo.setContextUser(user);

return new OAuth2RestClient(serverURL, tenantInfo);
}

/**
* Creates SCIM2RestClient for organization.
*/
private SCIM2RestClient createSCIM2RestClientForOrganization() throws IOException {

Tenant tenantInfo = new Tenant();
tenantInfo.setDomain(ORGANIZATION_HANDLE);
User user = new User();
user.setUserName("testadmin@" + ORGANIZATION_HANDLE);
tenantInfo.setContextUser(user);

return new SCIM2RestClient(serverURL, tenantInfo);
}
Copy link

@coderabbitai coderabbitai bot Dec 23, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "OAuth2RestClient.java" | head -5

Repository: wso2/product-is

Length of output: 195


🏁 Script executed:

find . -type f -name "SCIM2RestClient.java" | head -5

Repository: wso2/product-is

Length of output: 194


🏁 Script executed:

find . -type f -name "User.java" | head -10

Repository: wso2/product-is

Length of output: 195


🏁 Script executed:

find . -type f -name "Tenant.java" | head -5

Repository: wso2/product-is

Length of output: 197


🏁 Script executed:

cat -n ./modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java

Repository: wso2/product-is

Length of output: 50371


🏁 Script executed:

cat -n ./modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/SCIM2RestClient.java

Repository: wso2/product-is

Length of output: 50371


🏁 Script executed:

cat -n ./modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/serviceextensions/model/User.java

Repository: wso2/product-is

Length of output: 4352


🏁 Script executed:

cat -n ./modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/serviceextensions/model/Tenant.java

Repository: wso2/product-is

Length of output: 2271


🏁 Script executed:

find . -type f -path "*/carbon/automation/engine/context/beans/User.java"

Repository: wso2/product-is

Length of output: 41


🏁 Script executed:

find . -type f -path "*/carbon/automation/engine/context/beans/Tenant.java"

Repository: wso2/product-is

Length of output: 41


🏁 Script executed:

cat -n ./modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2AuthorizationForSubOrganizationAppsTestCase.java | sed -n '1050,1090p'

Repository: wso2/product-is

Length of output: 1804


🏁 Script executed:

rg "createOAuth2RestClientForOrganization\|createSCIM2RestClientForOrganization" --max-count=20

Repository: wso2/product-is

Length of output: 41


🏁 Script executed:

grep -r "new OAuth2RestClient\|new SCIM2RestClient" --include="*.java" | head -20

Repository: wso2/product-is

Length of output: 4717


🏁 Script executed:

sed -n '833,841p' ./modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java

Repository: wso2/product-is

Length of output: 486


🏁 Script executed:

sed -n '843,851p' ./modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/SCIM2RestClient.java

Repository: wso2/product-is

Length of output: 471


🏁 Script executed:

sed -n '1050,1100p' ./modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/SCIM2RestClient.java

Repository: wso2/product-is

Length of output: 1929


Set userDomain explicitly in REST client initialization for organization-specific tests

Both createOAuth2RestClientForOrganization and createSCIM2RestClientForOrganization set only the user name without populating userDomain. Since the REST client constructors derive tenant domain from getContextUser().getUserDomain() and immediately use it in .equals() comparisons within path builders, a null userDomain will cause NPE.

Add explicit user.setUserDomain(ORGANIZATION_HANDLE) calls:

Tenant tenantInfo = new Tenant();
tenantInfo.setDomain(ORGANIZATION_HANDLE);
User user = new User();
user.setUserName("testadmin@" + ORGANIZATION_HANDLE);
user.setUserDomain(ORGANIZATION_HANDLE);
tenantInfo.setContextUser(user);
🤖 Prompt for AI Agents
In
modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/oauth2/OAuth2AuthorizationForSubOrganizationAppsTestCase.java
around lines 1059 to 1082, both createOAuth2RestClientForOrganization and
createSCIM2RestClientForOrganization set only the userName on the User object
which leaves userDomain null and causes an NPE when the REST client reads
getContextUser().getUserDomain(); fix by setting the userDomain explicitly to
ORGANIZATION_HANDLE on the User object before calling
tenantInfo.setContextUser(user) (i.e., call
user.setUserDomain(ORGANIZATION_HANDLE) in both methods).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In these test cases the ORGANIZATION_HANDLE cannot be null in any cases. In the constructor, we are sending values for ORGANIZATION_HANDLE for each data set.

Copy link

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how OAuth2RestClient and SCIM2RestClient use tenantInfo and getUserDomain()
echo "=== OAuth2RestClient constructor usage of getUserDomain() ==="
rg -A 20 "public OAuth2RestClient\(" --type java | head -50

echo ""
echo "=== SCIM2RestClient constructor usage of getUserDomain() ==="
rg -A 20 "public SCIM2RestClient\(" --type java | head -50

echo ""
echo "=== Check other test files for User object initialization patterns ==="
rg "new User\(\)" --type java -A 5 -B 2 | grep -E "(setUserName|setUserDomain|setContextUser)" | head -40

Length of output: 11435


🏁 Script executed:

#!/bin/bash
# Check what the path builder methods do with tenantDomain parameter
echo "=== OAuth2RestClient path builder methods ==="
rg "getApplicationsPath|getSubOrgApplicationsPath|getAPIResourcesPath" --type java -A 10 modules/integration/tests-integration/tests-backend/src/test/java/org/wso2/identity/integration/test/restclients/OAuth2RestClient.java | head -80

echo ""
echo "=== Check if any test sets userDomain explicitly ==="
rg "setUserDomain" --type java | head -20

Length of output: 2177


@ShanChathusanda93, you're absolutely right that ORGANIZATION_HANDLE cannot be null—it's passed through the constructor and used throughout the test. However, the issue flagged by CodeRabbit is about a different field.

The problem is that in both createOAuth2RestClientForOrganization() and createSCIM2RestClientForOrganization() (lines 1059-1082), you create a new User object and call:

User user = new User();
user.setUserName("testadmin@" + ORGANIZATION_HANDLE);

But you never call user.setUserDomain(...), which means the userDomain field remains null.

When you pass this to the REST client constructors, here's what happens:

OAuth2RestClient constructor:

String tenantDomain = tenantInfo.getContextUser().getUserDomain();  // Returns null
applicationManagementApiBasePath = getApplicationsPath(backendUrl, tenantDomain);

SCIM2RestClient constructor:

this.tenantDomain = tenantInfo.getContextUser().getUserDomain();  // Returns null

The path builders then call:

if (tenantDomain.equals(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME)) {  // NPE here!

This will throw a NullPointerException because you're calling .equals() on a null reference.

The fix is simple—set the userDomain field explicitly:

User user = new User();
user.setUserName("testadmin@" + ORGANIZATION_HANDLE);
user.setUserDomain(ORGANIZATION_HANDLE);  // Add this line
tenantInfo.setContextUser(user);

This ensures that getUserDomain() returns the organization handle instead of null, and the REST clients can properly construct their paths.

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.

2 participants