Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Introducing Organization Administrator with refactor for Instance Admin #39417

Open
wants to merge 7 commits into
base: release
Choose a base branch
from

Conversation

trishaanand
Copy link
Contributor

@trishaanand trishaanand commented Feb 24, 2025

Description

To allow for partial organization related setting changes and not exposing instance level settings, introducing organization administrator role.

Fixes #Issue Number
or
Fixes Issue URL

Warning

If no issue exists, please create an issue first, and check with the maintainers if the issue is valid.

Automation

/test all

🔍 Cypress test results

Caution

🔴 🔴 🔴 Some tests have failed.
Workflow run: https://github.com/appsmithorg/appsmith/actions/runs/13516210763
Commit: d4f1f97
Cypress dashboard.
Tags: @tag.All
Spec:
The following are new failures, please fix them before merging the PR:

  1. cypress/e2e/Regression/Apps/PromisesApp_spec.js
  2. cypress/e2e/Regression/ClientSide/AdminSettings/Branding_settings_Spec.ts
  3. cypress/e2e/Regression/ClientSide/AdminSettings/Email_settings_Spec.ts
  4. cypress/e2e/Regression/ClientSide/AdminSettings/General_settings_Spec.ts
  5. cypress/e2e/Regression/ClientSide/AuditLogs/Audit_logs_spec.js
  6. cypress/e2e/Regression/ClientSide/Branding/Branding_spec.js
  7. cypress/e2e/Regression/ClientSide/ExplorerTests/Admin_settings_1_spec.js
  8. cypress/e2e/Regression/ClientSide/ExplorerTests/Admin_settings_2_spec.js
  9. cypress/e2e/Regression/ClientSide/ExplorerTests/Admin_settings_3_spec.ts
  10. cypress/e2e/Regression/ClientSide/Fork/ForkApplicationInDeployedMode_spec.ts
  11. cypress/e2e/Regression/ClientSide/Fork/ForkApplication_spec.ts
  12. cypress/e2e/Regression/ClientSide/FormLogin/EnableFormLogin_spec.js
  13. cypress/e2e/Regression/ClientSide/Google/EnableGoogle_spec.js
  14. cypress/e2e/Regression/ClientSide/Homepage/HomepageExperienceV2_spec.ts
  15. cypress/e2e/Regression/ClientSide/OtherUIFeatures/Analytics_spec.js
  16. cypress/e2e/Regression/ClientSide/OtherUIFeatures/ExportApplication_spec.js
  17. cypress/e2e/Regression/ClientSide/ProductRamps/PrivateEmbedRamp_spec.ts
  18. cypress/e2e/Regression/ClientSide/SettingsPane/SettingsPane_EmbedSettings_spec.ts
  19. cypress/e2e/Regression/ClientSide/Workspace/DeleteWorkspace_spec.ts
  20. cypress/e2e/Regression/ClientSide/Workspace/MemberRoles_Spec.ts
  21. cypress/e2e/Regression/Enterprise/AdminSettings/Admin_Settings_NoAccess_spec.js
List of identified flaky tests.
Tue, 25 Feb 2025 10:20:25 UTC

Communication

Should the DevRel and Marketing teams inform users about this change?

  • Yes
  • No

Summary by CodeRabbit

  • New Features
    • Introduced an "Organization Administrator" role to manage organizational permissions.
  • Refactor
    • Renamed role designations from "Tenant Admin" to "Organization Admin" and from "Super User" to "Instance Administrator" in user management.
    • Enhanced the user role management logic to focus on organization-level permissions.
  • Chores
    • Implemented a database migration to update and realign permission groups.
  • Tests
    • Updated and added tests to ensure proper assignment and removal of the new administrative roles.

Copy link
Contributor

coderabbitai bot commented Feb 24, 2025

Walkthrough

This pull request refactors role and permission management across the application. The legacy TENANT_ADMIN role is replaced by ORGANIZATION_ADMIN and corresponding instance administrator methods. New constants and methods are introduced while existing ones are renamed and removed. Database migrations and query logic are updated accordingly, and test cases are modified to validate the new permission flows. Additionally, dependency injection changes (adding CommonConfig) are applied across utility and service classes.

Changes

File(s) Change Summary
app/server/.../AppsmithRole.java, app/server/.../FieldNameCE.java Replaced TENANT_ADMIN with ORGANIZATION_ADMIN in the role enum and added the constant ORGANIZATION_ADMINISTRATOR_ROLE.
app/server/.../UserUtils.java, app/server/.../UserUtilsCE.java, app/server/.../UserServiceCEImpl.java, app/server/.../EnvManagerCEImpl.java, app/server/.../UserSignupCEImpl.java Renamed role assignment methods from makeSuperUser to makeInstanceAdministrator, added removeInstanceAdmin, and updated the constructor to include CommonConfig.
app/server/.../DatabaseChangelog2.java, app/server/.../Migration066_RenameInstanceAdminRoleToOrgAdmin.java Updated migration scripts to replace legacy role references with the new organization admin roles and renamed permission groups accordingly.
app/server/.../CacheableRepositoryHelperCE.java, app/server/.../CacheableRepositoryHelperCEImpl.java Removed the getInstanceAdminPermissionGroupId method and simplified query logic to support the new organization admin role.
Test files (.../UserUtilsTest.java, .../CacheableRepositoryTest.java, .../PermissionGroupServiceTest.java, .../OrganizationServiceCETest.java) Revised tests to replace calls to makeSuperUser with makeInstanceAdministrator and to verify the updated permission group assignments.

Sequence Diagram(s)

sequenceDiagram
    participant U as User
    participant Signup as UserSignupCEImpl
    participant Utils as UserUtils
    participant DB as Database
    U->>Signup: Signup request
    Signup->>Utils: makeInstanceAdministrator(user)
    Utils->>DB: Assign permissions to instance/organization admin groups
    DB-->>Utils: Confirmation
    Utils-->>Signup: Success
    Signup-->>U: Signup complete
Loading
sequenceDiagram
    participant M as Migration066_RenameInstanceAdminRoleToOrgAdmin
    participant CT as Config Collection
    participant ORG as Organization Collection
    participant PG as Permission Group Collection
    M->>CT: Retrieve "instanceConfig"
    CT-->>M: Config data
    M->>ORG: Fetch default organization
    ORG-->>M: Organization data
    M->>PG: Rename permission group to Organization Admin
    PG-->>M: Update result
    M->>PG: Create new role entry
    PG-->>M: Role created
Loading

Possibly related PRs

  • fix: Super user cache eviction when user is added via env variable #37785: The changes in the main PR, specifically the modification of the TENANT_ADMIN role to ORGANIZATION_ADMIN, are directly related to the changes in the retrieved PR, which also involves updates to the DatabaseChangelog2 class that includes permission management for instance administrators. Both PRs focus on the transition from tenant-based to organization-based role management.
  • feat: Introduce Organization Administrator concept along with Instance Administrator #39381: The changes in the main PR are directly related to those in the retrieved PR, as both involve the removal of the TENANT_ADMIN role and its replacement with the ORGANIZATION_ADMIN role in the AppsmithRole enum, along with the introduction of the ORGANIZATION_ADMINISTRATOR_ROLE constant.

Suggested labels

Admin Settings Product, Bug

Suggested reviewers

  • sharat87
  • AnaghHegde
  • abhvsn

Poem

In code we trust, a change so neat,
Roles reborn with a brand new beat.
From tenant to organization, clear and bright,
Permissions aligned, all set just right.
Tests and migrations now sing in tune,
A fresh new chapter under our dev moon! 🎉

✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added the Enhancement New feature or request label Feb 24, 2025
Copy link
Contributor

@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

🔭 Outside diff range comments (1)
app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/UserUtilsCE.java (1)

83-119: 🛠️ Refactor suggestion

Potential concurrency considerations
When updating permission groups, consider scenarios with concurrent calls. If two calls modify the same permission group simultaneously, the last update wins might overwrite changes from the other. A more atomic approach with add/remove style mutation or version checks could help mitigate race conditions.

🧹 Nitpick comments (6)
app/server/appsmith-server/src/main/java/com/appsmith/server/acl/AppsmithRole.java (2)

66-66: Add a description for the ORGANIZATION_ADMIN role.

While the role implementation is correct, consider adding a descriptive comment to document the role's purpose and responsibilities.

-    ORGANIZATION_ADMIN(ORGANIZATION_ADMINISTRATOR_ROLE, "", Set.of(MANAGE_ORGANIZATION)),
+    ORGANIZATION_ADMIN(
+            ORGANIZATION_ADMINISTRATOR_ROLE,
+            "Role for managing organization-level settings and configurations",
+            Set.of(MANAGE_ORGANIZATION)),

66-66: Add a description for the ORGANIZATION_ADMIN role.

The empty description string reduces code maintainability. Consider adding a meaningful description that explains the role's purpose and responsibilities.

-    ORGANIZATION_ADMIN(ORGANIZATION_ADMINISTRATOR_ROLE, "", Set.of(MANAGE_ORGANIZATION)),
+    ORGANIZATION_ADMIN(
+            ORGANIZATION_ADMINISTRATOR_ROLE,
+            "Role for managing organization-level settings and configurations",
+            Set.of(MANAGE_ORGANIZATION)),
app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CacheableRepositoryHelperCEImpl.java (1)

62-85: Consider adding an index for more efficient lookups.

In this query, fields like assignedToUserIds are used in a filter condition. To improve performance for large user bases, ensure that PermissionGroup has an index on assignedToUserIds (as well as any other relevant fields).

app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration066_RenameInstanceAdminRoleToOrgAdmin.java (1)

1-121: Add tests or verification steps for the migration.

The migration logic appears sound, but because it modifies critical permissions, consider automated tests or scripts to ensure data integrity. Additionally, the rollback section remains empty—implementing a partial or manual rollback strategy might be beneficial in case of environment-specific failures.

Would you like help creating a test or verification strategy for this migration?

app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/UserUtilsCE.java (2)

70-77: Conditional handling of organization admin
Skipping the organization admin assignment in cloud hosting scenarios may be correct for your design. Ensure this condition is documented for clarity and to avoid confusion among future maintainers.


174-185: Clear data retrieval methods
The getInstanceAdminPermissionGroup and getOrganizationAdminPermissionGroup methods centralize permission group logic. This modular approach enhances reusability and keeps your code DRY. Consider improving inline comments for future maintainers.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f3eca9b and eb22777.

📒 Files selected for processing (15)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/acl/AppsmithRole.java (2 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/FieldNameCE.java (1 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/UserUtils.java (2 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/UserUtilsCE.java (3 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/DatabaseChangelog2.java (2 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration066_RenameInstanceAdminRoleToOrgAdmin.java (1 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CacheableRepositoryHelperCE.java (0 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CacheableRepositoryHelperCEImpl.java (1 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/UserServiceCEImpl.java (1 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/EnvManagerCEImpl.java (2 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/UserSignupCEImpl.java (1 hunks)
  • app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/UserUtilsTest.java (1 hunks)
  • app/server/appsmith-server/src/test/java/com/appsmith/server/repositories/CacheableRepositoryTest.java (1 hunks)
  • app/server/appsmith-server/src/test/java/com/appsmith/server/services/PermissionGroupServiceTest.java (1 hunks)
  • app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/OrganizationServiceCETest.java (1 hunks)
💤 Files with no reviewable changes (1)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CacheableRepositoryHelperCE.java
✅ Files skipped from review due to trivial changes (1)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/constants/ce/FieldNameCE.java
👮 Files not reviewed due to content moderation or server errors (4)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/DatabaseChangelog2.java
  • app/server/appsmith-server/src/main/java/com/appsmith/server/acl/AppsmithRole.java
  • app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/UserSignupCEImpl.java
  • app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/EnvManagerCEImpl.java
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: server-spotless / spotless-check
  • GitHub Check: server-unit-tests / server-unit-tests
🔇 Additional comments (21)
app/server/appsmith-server/src/test/java/com/appsmith/server/services/PermissionGroupServiceTest.java (1)

99-99: LGTM! Role transition implemented correctly.

The change from makeSuperUser to makeInstanceAdministrator aligns with the new role management system.

app/server/appsmith-server/src/main/java/com/appsmith/server/services/ce/UserServiceCEImpl.java (1)

420-422: LGTM! Role assignment updated correctly.

The change from makeSuperUser to makeInstanceAdministrator maintains consistent role management throughout the service layer.

app/server/appsmith-server/src/test/java/com/appsmith/server/services/ce/OrganizationServiceCETest.java (1)

104-104: LGTM! Test setup updated appropriately.

The change from makeSuperUser to makeInstanceAdministrator correctly sets up the test environment for organization admin functionality.

app/server/appsmith-server/src/test/java/com/appsmith/server/repositories/CacheableRepositoryTest.java (1)

43-43: LGTM! Test setup updated correctly.

The change from makeSuperUser to makeInstanceAdministrator maintains consistency in role management across test cases.

app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/DatabaseChangelog2.java (3)

63-63:

❓ Verification inconclusive

LGTM! Verify the migration's impact on existing tenant admins.

The role change from TENANT_ADMIN to ORGANIZATION_ADMIN looks correct. The permissions are preserved during the migration.

Run this script to verify that existing tenant admins are properly migrated:

Also applies to: 553-558


🏁 Script executed:

#!/bin/bash
# Description: Check if any users still have the old TENANT_ADMIN role
# and verify that ORGANIZATION_ADMIN permissions are correctly assigned

# Search for any remaining TENANT_ADMIN references
rg "TENANT_ADMIN" -A 5

# Check permission assignments in the database
ast-grep --pattern $'class PermissionGroup {
  $$$
  permissions {
    $$$
    TENANT_ADMIN
    $$$
  }
  $$$
}'

Length of output: 136


Manual Verification Needed:
The change from TENANT_ADMIN to ORGANIZATION_ADMIN appears correct, and the initial script did not reveal any lingering instances of TENANT_ADMIN. However, since the script didn’t produce any output—which could be indicative of low inference quality—it’s important to manually verify that all legacy references have been removed and that the new role permissions are correctly applied.

Action Items:

  • Use a comprehensive search (e.g., rg -w "TENANT_ADMIN") across the codebase to ensure no references to the old role remain.
  • Confirm that instances expecting tenant admin permissions now properly reference ORGANIZATION_ADMIN (for example, in permission assignment sections within relevant classes).

63-63: LGTM! Role transition implemented correctly.

The changes correctly update the role reference from TENANT_ADMIN to ORGANIZATION_ADMIN for permission management.

Also applies to: 553-553


63-63:

❓ Verification inconclusive

LGTM! Verify the migration's impact on existing tenant admins.

The role change from TENANT_ADMIN to ORGANIZATION_ADMIN looks correct. The permissions are preserved during the migration.

Run this script to verify that existing tenant admins are properly migrated:

Also applies to: 553-558


🏁 Script executed:

#!/bin/bash
# Description: Check for any remaining references to TENANT_ADMIN and verify permission groups
# Search for any remaining TENANT_ADMIN references
rg "TENANT_ADMIN"

# Search for permission groups to verify the migration
ast-grep --pattern $'class PermissionGroup {
  $$$
  name = "Organization Administrator Role"
  $$$
}'

Length of output: 121


Subject: Verify the Migration’s Impact on Tenant Admins

The update replacing TENANT_ADMIN with ORGANIZATION_ADMIN appears correct (see the static import change). However, the automated searches (using rg and ast-grep) didn’t return any output, so it’s not clear whether these commands fully confirm the proper migration.

  • Please manually verify that no references to TENANT_ADMIN remain in the codebase.
  • Confirm that the permission group now correctly includes the "Organization Administrator Role" (as also noted for lines 553-558).

Thanks for ensuring that tenant admin permissions remain preserved during migration.

app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/UserSignupCEImpl.java (3)

293-293: LGTM! Method name better reflects its purpose.

The renaming from makeSuperUser to makeInstanceAdministrator improves code clarity by accurately describing the role being assigned.


293-293: LGTM! Method name improved for clarity.

The renaming from makeSuperUser to makeInstanceAdministrator better reflects the method's purpose and aligns with the role transition.


293-293: LGTM! Method name better reflects its purpose.

The change from makeSuperUser to makeInstanceAdministrator improves code clarity by accurately describing the role being assigned.

app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ce/EnvManagerCEImpl.java (3)

614-614: LGTM! Method names better reflect their purposes.

The renaming from removeSuperUser/makeSuperUser to removeInstanceAdmin/makeInstanceAdministrator improves code clarity by accurately describing the roles being managed.

Also applies to: 647-647


614-614: LGTM! Method names updated consistently.

The method renames maintain consistency with the role transition from super user to instance administrator.

Also applies to: 647-647


614-614: LGTM! Method names align with the new role system.

The changes from removeSuperUser/makeSuperUser to removeInstanceAdmin/makeInstanceAdministrator improve code clarity by accurately describing the roles being managed.

Also applies to: 647-647

app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/UserUtils.java (2)

3-3: Import addition looks good.


20-27: Verify the usage of CommonConfig defaults.

Ensure the constructor usage of CommonConfig properly handles default values within the class. Confirm that any optional or fallback behaviors in CommonConfig are covered, particularly if environment variables are missing or not set.

app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/UserUtilsTest.java (4)

31-43: Good initialization of test data
The setup method creates distinct users with different IDs, ensuring no conflicts in subsequent tests. This approach is clear, concise, and sets the stage for more straightforward assertions.


45-69: Comprehensive validation of instance and organization admin assignments
The test effectively uses StepVerifier to verify that both users are assigned to the instance admin and organization admin permission groups. The additional check on assigned user IDs is thorough.


71-97: Robust removal test
The test adds followed by removing admin permissions in one flow, ensuring both steps are valid. Testing real-world usage scenarios like add-then-remove in one test prevents regressions.


99-126: Duplicate assignment check
Ensuring that a user is not re-added multiple times is critical. The test's final assertion of exactly one assigned user in each group removes ambiguity.

app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/UserUtilsCE.java (1)

121-172: Symmetry in addition and removal methods
The removeInstanceAdmin method closely mirrors makeInstanceAdministrator, maintaining consistent logic. This is good for readability and maintainability.

app/server/appsmith-server/src/main/java/com/appsmith/server/acl/AppsmithRole.java (1)

28-28: LGTM! Role definition is clear and maintains permissions.

The ORGANIZATION_ADMIN role correctly replaces TENANT_ADMIN while preserving the MANAGE_ORGANIZATION permission.

Also applies to: 66-66

@trishaanand trishaanand added the ok-to-test Required label for CI label Feb 24, 2025
Copy link
Contributor

@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: 2

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb22777 and 79e6e57.

📒 Files selected for processing (2)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CacheableRepositoryHelperCE.java (1 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CacheableRepositoryHelperCEImpl.java (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CacheableRepositoryHelperCE.java
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: build
  • GitHub Check: server-spotless / spotless-check
  • GitHub Check: server-unit-tests / server-unit-tests
🔇 Additional comments (1)
app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CacheableRepositoryHelperCEImpl.java (1)

58-59: LGTM! Additional null check for organizationId.

The added null check for organizationId aligns with the new organization-based permission model.

Comment on lines +63 to +86
Mono<Query> createQueryMono = getOrganizationAdminPermissionGroupId(user.getOrganizationId())
.map(organizationAdminPermissionGroupId -> {
BridgeQuery<PermissionGroup> assignedToUserIdsCriteria =
Bridge.equal(PermissionGroup.Fields.assignedToUserIds, user.getId());

BridgeQuery<PermissionGroup> notDeletedCriteria = notDeleted();
BridgeQuery<PermissionGroup> notDeletedCriteria = notDeleted();

// The roles should be either workspace default roles, user management role, or instance admin role
BridgeQuery<PermissionGroup> ceSupportedRolesCriteria = Bridge.or(
Bridge.equal(PermissionGroup.Fields.defaultDomainType, Workspace.class.getSimpleName()),
Bridge.equal(PermissionGroup.Fields.defaultDomainType, User.class.getSimpleName()),
Bridge.equal(PermissionGroup.Fields.id, instanceAdminPermissionGroupId));
// The roles should be either workspace default roles, user management role, or instance admin role
BridgeQuery<PermissionGroup> ceSupportedRolesCriteria = Bridge.or(
Bridge.equal(PermissionGroup.Fields.defaultDomainType, Workspace.class.getSimpleName()),
Bridge.equal(PermissionGroup.Fields.defaultDomainType, User.class.getSimpleName()),
Bridge.equal(PermissionGroup.Fields.id, organizationAdminPermissionGroupId));

BridgeQuery<PermissionGroup> andCriteria =
Bridge.and(assignedToUserIdsCriteria, notDeletedCriteria, ceSupportedRolesCriteria);
BridgeQuery<PermissionGroup> andCriteria =
Bridge.and(assignedToUserIdsCriteria, notDeletedCriteria, ceSupportedRolesCriteria);

Query query = new Query();
query.addCriteria(andCriteria);
Query query = new Query();
query.addCriteria(andCriteria);

// Since we are only interested in the permission group ids, we can project only the id field.
query.fields().include(PermissionGroup.Fields.id);
// Since we are only interested in the permission group ids, we can project only the id field.
query.fields().include(PermissionGroup.Fields.id);

return query;
});
return query;
});
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify error handling for missing organization admin permission group.

The query construction looks good, but we should handle the case where getOrganizationAdminPermissionGroupId returns empty.


🏁 Script executed:

#!/bin/bash
# Search for error handling of empty organization admin permission group
rg -A 5 "getOrganizationAdminPermissionGroupId.*empty|flatMap.*error"

Length of output: 13241


Action: Implement Error Handling for Empty Admin Permission Group

The query construction in CacheableRepositoryHelperCEImpl.java (lines 63–86) currently maps the result of getOrganizationAdminPermissionGroupId(user.getOrganizationId()) directly without handling the scenario where the returned Mono might be empty. Similar patterns in the codebase (e.g., in DatasourceStorageServiceCEImpl.java and CommonGitServiceCEImpl.java) use error handling (via flatMap with Mono.error or similar operators) to deal with such cases. To ensure robust behavior and avoid potential downstream issues, please add error or empty value handling (for example, using switchIfEmpty or onErrorResume) for when the organization admin permission group is not found.

  • File: app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CacheableRepositoryHelperCEImpl.java
  • Lines: 63–86

Comment on lines 96 to 113
public Mono<String> getOrganizationAdminPermissionGroupId(String organizationId) {

// Find the permission group id of the organization admin
BridgeQuery<PermissionGroup> andCriteria = Bridge.and(
Bridge.equal(PermissionGroup.Fields.defaultDomainType, Organization.class.getSimpleName()),
Bridge.equal(PermissionGroup.Fields.defaultDomainId, organizationId));

Query query = new Query();
query.addCriteria(andCriteria);

// Since we are only interested in the permission group ids, we can project only the id field.
query.fields().include(PermissionGroup.Fields.id);

return mongoOperations
.find(query, PermissionGroup.class)
.map(permissionGroup -> permissionGroup.getId())
.next();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for missing organization admin permission group.

The implementation looks good but should handle the case where no permission group is found.

 return mongoOperations
         .find(query, PermissionGroup.class)
         .map(permissionGroup -> permissionGroup.getId())
-        .next();
+        .next()
+        .switchIfEmpty(Mono.error(
+            new AppsmithException(
+                AppsmithError.NO_ORGANIZATION_ADMIN_PERMISSION_GROUP,
+                organizationId
+            )
+        ));
📝 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
public Mono<String> getOrganizationAdminPermissionGroupId(String organizationId) {
// Find the permission group id of the organization admin
BridgeQuery<PermissionGroup> andCriteria = Bridge.and(
Bridge.equal(PermissionGroup.Fields.defaultDomainType, Organization.class.getSimpleName()),
Bridge.equal(PermissionGroup.Fields.defaultDomainId, organizationId));
Query query = new Query();
query.addCriteria(andCriteria);
// Since we are only interested in the permission group ids, we can project only the id field.
query.fields().include(PermissionGroup.Fields.id);
return mongoOperations
.find(query, PermissionGroup.class)
.map(permissionGroup -> permissionGroup.getId())
.next();
}
public Mono<String> getOrganizationAdminPermissionGroupId(String organizationId) {
// Find the permission group id of the organization admin
BridgeQuery<PermissionGroup> andCriteria = Bridge.and(
Bridge.equal(PermissionGroup.Fields.defaultDomainType, Organization.class.getSimpleName()),
Bridge.equal(PermissionGroup.Fields.defaultDomainId, organizationId));
Query query = new Query();
query.addCriteria(andCriteria);
// Since we are only interested in the permission group ids, we can project only the id field.
query.fields().include(PermissionGroup.Fields.id);
return mongoOperations
.find(query, PermissionGroup.class)
.map(permissionGroup -> permissionGroup.getId())
.next()
.switchIfEmpty(Mono.error(
new AppsmithException(
AppsmithError.NO_ORGANIZATION_ADMIN_PERMISSION_GROUP,
organizationId
)
));
}

Copy link
Contributor

@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 (1)
app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CacheableRepositoryHelperCEImpl.java (1)

97-123: ⚠️ Potential issue

Add error handling for missing organization admin permission group.

The method should handle the case where no permission group is found.

        return mongoOperations
                .find(query, PermissionGroup.class)
                .map(permissionGroup -> permissionGroup.getId())
                .next()
+               .switchIfEmpty(Mono.error(
+                   new AppsmithException(
+                       AppsmithError.NO_ORGANIZATION_ADMIN_PERMISSION_GROUP,
+                       organizationId
+                   )
+               ))
                .doOnSuccess(
                        permissionGroupId -> inMemoryCacheableRepositoryHelper.setOrganizationAdminPermissionGroupId(
                                organizationId, permissionGroupId));
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 79e6e57 and e02360c.

📒 Files selected for processing (2)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/InMemoryCacheableRepositoryHelper.java (3 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CacheableRepositoryHelperCEImpl.java (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: server-unit-tests / server-unit-tests
  • GitHub Check: server-spotless / spotless-check
🔇 Additional comments (3)
app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/InMemoryCacheableRepositoryHelper.java (1)

43-49: LGTM! Clean implementation of getter and setter methods.

The methods follow the established pattern in the class and provide clear functionality for managing organization admin permission group IDs.

app/server/appsmith-server/src/main/java/com/appsmith/server/repositories/ce/CacheableRepositoryHelperCEImpl.java (2)

56-62: LGTM! Comprehensive validation of user attributes.

The validation now includes organization ID, which is essential for the new organization-based permission model.


64-87: LGTM! Well-structured query construction.

The query construction is clean and modular, with clear separation of criteria.

@@ -12,6 +14,8 @@ public class InMemoryCacheableRepositoryHelper {

private String instanceAdminPermissionGroupId = null;

private Map<String, String> inMemoryOrganizationIdOrganizationPermissionGroupIdMap = new HashMap<>();
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using ConcurrentHashMap for thread safety.

The HashMap implementation might lead to race conditions in a multi-threaded environment.

-    private Map<String, String> inMemoryOrganizationIdOrganizationPermissionGroupIdMap = new HashMap<>();
+    private Map<String, String> inMemoryOrganizationIdOrganizationPermissionGroupIdMap = new ConcurrentHashMap<>();
📝 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
private Map<String, String> inMemoryOrganizationIdOrganizationPermissionGroupIdMap = new HashMap<>();
private Map<String, String> inMemoryOrganizationIdOrganizationPermissionGroupIdMap = new ConcurrentHashMap<>();

Copy link
Contributor

@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

🔭 Outside diff range comments (1)
app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/UserUtilsCE.java (1)

70-121: 🛠️ Refactor suggestion

Consider adding validation for minimum number of instance administrators.

The method correctly handles permission group assignments and cache eviction. However, it's important to ensure that there's always at least one instance administrator to prevent lockout scenarios.

Consider adding a check to ensure at least one instance administrator remains:

 public Mono<Boolean> makeInstanceAdministrator(List<User> users) {
+    if (users == null || users.isEmpty()) {
+        return Mono.error(new IllegalArgumentException("Users list cannot be empty"));
+    }
     // Rest of the method...
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e02360c and d99137c.

📒 Files selected for processing (2)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/UserUtilsCE.java (3 hunks)
  • app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/UserUtilsTest.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/server/appsmith-server/src/test/java/com/appsmith/server/helpers/UserUtilsTest.java
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: server-spotless / spotless-check
  • GitHub Check: server-unit-tests / server-unit-tests
🔇 Additional comments (2)
app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/UserUtilsCE.java (2)

4-9: LGTM! Dependencies and constructor changes are well-structured.

The new dependencies are properly injected and initialized in the constructor, following good dependency injection practices.

Also applies to: 37-39, 41-52


176-206: LGTM! Well-implemented caching for permission groups.

The permission group retrieval methods effectively use caching to optimize performance while maintaining data consistency.

Comment on lines +123 to +174
return savedConfigAndPermissionGroupMono.map(Tuple2::getT2);
}
if (!cloudHosting) {
organizationAdminPgMono = getDefaultOrganizationAdminPermissionGroup();
}

protected Mono<Tuple2<PermissionGroup, Config>> createConfigAndPermissionGroupForSuperAdmin() {
return Mono.zip(createInstanceAdminConfigObject(), createInstanceAdminPermissionGroupWithoutPermissions())
return Mono.zip(getInstanceAdminPermissionGroup(), organizationAdminPgMono)
.flatMap(tuple -> {
Config savedInstanceConfig = tuple.getT1();
PermissionGroup savedPermissionGroup = tuple.getT2();

// Update the instance config with the permission group id
savedInstanceConfig.setConfig(
new JSONObject(Map.of(DEFAULT_PERMISSION_GROUP, savedPermissionGroup.getId())));

Policy editConfigPolicy = Policy.builder()
.permission(MANAGE_INSTANCE_CONFIGURATION.getValue())
.permissionGroups(Set.of(savedPermissionGroup.getId()))
.build();
Policy readConfigPolicy = Policy.builder()
.permission(READ_INSTANCE_CONFIGURATION.getValue())
.permissionGroups(Set.of(savedPermissionGroup.getId()))
.build();

savedInstanceConfig.setPolicies(Set.of(editConfigPolicy, readConfigPolicy));

// Add config permissions to permission group
Set<Permission> configPermissions =
Set.of(new Permission(savedInstanceConfig.getId(), MANAGE_INSTANCE_CONFIGURATION));

return Mono.zip(
addPermissionsToPermissionGroup(savedPermissionGroup, configPermissions),
configRepository.save(savedInstanceConfig));
});
}
PermissionGroup instanceAdminPg = tuple.getT1();
PermissionGroup organizationAdminPg = tuple.getT2();

private Mono<Config> createInstanceAdminConfigObject() {
Config instanceAdminConfiguration = new Config();
instanceAdminConfiguration.setName(FieldName.INSTANCE_CONFIG);

return configRepository.save(instanceAdminConfiguration);
}
if (instanceAdminPg.getAssignedToUserIds() == null) {
instanceAdminPg.setAssignedToUserIds(new HashSet<>());
}
Set<String> assignedToUserIds = new HashSet<>(instanceAdminPg.getAssignedToUserIds());
assignedToUserIds.removeAll(users.stream().map(User::getId).collect(Collectors.toList()));

private Mono<PermissionGroup> createInstanceAdminPermissionGroupWithoutPermissions() {
PermissionGroup instanceAdminPermissionGroup = new PermissionGroup();
instanceAdminPermissionGroup.setName(FieldName.INSTANCE_ADMIN_ROLE);
BridgeUpdate updateObj = Bridge.update();
String path = PermissionGroup.Fields.assignedToUserIds;

return permissionGroupRepository.save(instanceAdminPermissionGroup).flatMap(savedPermissionGroup -> {
Set<Permission> permissions = Set.of(
new Permission(savedPermissionGroup.getId(), READ_PERMISSION_GROUP_MEMBERS),
new Permission(savedPermissionGroup.getId(), ASSIGN_PERMISSION_GROUPS),
new Permission(savedPermissionGroup.getId(), UNASSIGN_PERMISSION_GROUPS));
savedPermissionGroup.setPermissions(permissions);
updateObj.set(path, assignedToUserIds);
// Make Instance Admin is called before the first administrator is created.
Mono<Integer> updateInstanceAdminPgAssignmentMono =
permissionGroupRepository.updateById(instanceAdminPg.getId(), updateObj);

Policy readPermissionGroupPolicy = Policy.builder()
.permission(READ_PERMISSION_GROUP_MEMBERS.getValue())
.permissionGroups(Set.of(savedPermissionGroup.getId()))
.build();
if (!hasLength(organizationAdminPg.getId())) {
return updateInstanceAdminPgAssignmentMono;
}

Policy assignPermissionGroupPolicy = Policy.builder()
.permission(ASSIGN_PERMISSION_GROUPS.getValue())
.permissionGroups(Set.of(savedPermissionGroup.getId()))
.build();
// Also unassign the users from the organization admin permission group
Set<String> organizationAdminAssignedToUserIds = new HashSet<>();
if (organizationAdminPg.getAssignedToUserIds() != null) {
organizationAdminAssignedToUserIds.addAll(organizationAdminPg.getAssignedToUserIds());
}
organizationAdminAssignedToUserIds.removeAll(
users.stream().map(User::getId).collect(Collectors.toList()));
BridgeUpdate updateObj2 = Bridge.update();
String path2 = PermissionGroup.Fields.assignedToUserIds;
updateObj2.set(path2, organizationAdminAssignedToUserIds);
return updateInstanceAdminPgAssignmentMono.then(
permissionGroupRepository.updateById(organizationAdminPg.getId(), updateObj2));
})
.then(Mono.just(users))
.flatMapMany(Flux::fromIterable)
.flatMap(user -> permissionGroupRepository.evictAllPermissionGroupCachesForUser(
user.getEmail(), user.getOrganizationId()))
.then(Mono.just(Boolean.TRUE));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add safeguard against removing all instance administrators.

Similar to the makeInstanceAdministrator method, we should prevent removing all instance administrators.

Add validation to prevent removing all administrators:

 public Mono<Boolean> removeInstanceAdmin(List<User> users) {
+    return getInstanceAdminPermissionGroup()
+        .filter(pg -> pg.getAssignedToUserIds() != null && 
+            pg.getAssignedToUserIds().size() > users.size())
+        .switchIfEmpty(Mono.error(
+            new IllegalStateException("Cannot remove all instance administrators")))
+        .then(Mono.defer(() -> {
             // Existing method implementation
+        }));
📝 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
public Mono<Boolean> removeInstanceAdmin(List<User> users) {
Mono<Tuple2<PermissionGroup, Config>> savedConfigAndPermissionGroupMono =
createConfigAndPermissionGroupForSuperAdmin();
// TODO : Replace cloud hosting check with a feature flag check for multi tenancy
boolean cloudHosting = commonConfig.isCloudHosting();
Mono<PermissionGroup> organizationAdminPgMono = Mono.just(new PermissionGroup());
// return the saved instance config
return savedConfigAndPermissionGroupMono.map(Tuple2::getT2);
}
if (!cloudHosting) {
organizationAdminPgMono = getDefaultOrganizationAdminPermissionGroup();
}
protected Mono<Tuple2<PermissionGroup, Config>> createConfigAndPermissionGroupForSuperAdmin() {
return Mono.zip(createInstanceAdminConfigObject(), createInstanceAdminPermissionGroupWithoutPermissions())
return Mono.zip(getInstanceAdminPermissionGroup(), organizationAdminPgMono)
.flatMap(tuple -> {
Config savedInstanceConfig = tuple.getT1();
PermissionGroup savedPermissionGroup = tuple.getT2();
// Update the instance config with the permission group id
savedInstanceConfig.setConfig(
new JSONObject(Map.of(DEFAULT_PERMISSION_GROUP, savedPermissionGroup.getId())));
Policy editConfigPolicy = Policy.builder()
.permission(MANAGE_INSTANCE_CONFIGURATION.getValue())
.permissionGroups(Set.of(savedPermissionGroup.getId()))
.build();
Policy readConfigPolicy = Policy.builder()
.permission(READ_INSTANCE_CONFIGURATION.getValue())
.permissionGroups(Set.of(savedPermissionGroup.getId()))
.build();
savedInstanceConfig.setPolicies(Set.of(editConfigPolicy, readConfigPolicy));
// Add config permissions to permission group
Set<Permission> configPermissions =
Set.of(new Permission(savedInstanceConfig.getId(), MANAGE_INSTANCE_CONFIGURATION));
return Mono.zip(
addPermissionsToPermissionGroup(savedPermissionGroup, configPermissions),
configRepository.save(savedInstanceConfig));
});
}
PermissionGroup instanceAdminPg = tuple.getT1();
PermissionGroup organizationAdminPg = tuple.getT2();
private Mono<Config> createInstanceAdminConfigObject() {
Config instanceAdminConfiguration = new Config();
instanceAdminConfiguration.setName(FieldName.INSTANCE_CONFIG);
return configRepository.save(instanceAdminConfiguration);
}
if (instanceAdminPg.getAssignedToUserIds() == null) {
instanceAdminPg.setAssignedToUserIds(new HashSet<>());
}
Set<String> assignedToUserIds = new HashSet<>(instanceAdminPg.getAssignedToUserIds());
assignedToUserIds.removeAll(users.stream().map(User::getId).collect(Collectors.toList()));
private Mono<PermissionGroup> createInstanceAdminPermissionGroupWithoutPermissions() {
PermissionGroup instanceAdminPermissionGroup = new PermissionGroup();
instanceAdminPermissionGroup.setName(FieldName.INSTANCE_ADMIN_ROLE);
BridgeUpdate updateObj = Bridge.update();
String path = PermissionGroup.Fields.assignedToUserIds;
return permissionGroupRepository.save(instanceAdminPermissionGroup).flatMap(savedPermissionGroup -> {
Set<Permission> permissions = Set.of(
new Permission(savedPermissionGroup.getId(), READ_PERMISSION_GROUP_MEMBERS),
new Permission(savedPermissionGroup.getId(), ASSIGN_PERMISSION_GROUPS),
new Permission(savedPermissionGroup.getId(), UNASSIGN_PERMISSION_GROUPS));
savedPermissionGroup.setPermissions(permissions);
updateObj.set(path, assignedToUserIds);
// Make Instance Admin is called before the first administrator is created.
Mono<Integer> updateInstanceAdminPgAssignmentMono =
permissionGroupRepository.updateById(instanceAdminPg.getId(), updateObj);
Policy readPermissionGroupPolicy = Policy.builder()
.permission(READ_PERMISSION_GROUP_MEMBERS.getValue())
.permissionGroups(Set.of(savedPermissionGroup.getId()))
.build();
if (!hasLength(organizationAdminPg.getId())) {
return updateInstanceAdminPgAssignmentMono;
}
Policy assignPermissionGroupPolicy = Policy.builder()
.permission(ASSIGN_PERMISSION_GROUPS.getValue())
.permissionGroups(Set.of(savedPermissionGroup.getId()))
.build();
// Also unassign the users from the organization admin permission group
Set<String> organizationAdminAssignedToUserIds = new HashSet<>();
if (organizationAdminPg.getAssignedToUserIds() != null) {
organizationAdminAssignedToUserIds.addAll(organizationAdminPg.getAssignedToUserIds());
}
organizationAdminAssignedToUserIds.removeAll(
users.stream().map(User::getId).collect(Collectors.toList()));
BridgeUpdate updateObj2 = Bridge.update();
String path2 = PermissionGroup.Fields.assignedToUserIds;
updateObj2.set(path2, organizationAdminAssignedToUserIds);
return updateInstanceAdminPgAssignmentMono.then(
permissionGroupRepository.updateById(organizationAdminPg.getId(), updateObj2));
})
.then(Mono.just(users))
.flatMapMany(Flux::fromIterable)
.flatMap(user -> permissionGroupRepository.evictAllPermissionGroupCachesForUser(
user.getEmail(), user.getOrganizationId()))
.then(Mono.just(Boolean.TRUE));
}
public Mono<Boolean> removeInstanceAdmin(List<User> users) {
return getInstanceAdminPermissionGroup()
.filter(pg -> pg.getAssignedToUserIds() != null &&
pg.getAssignedToUserIds().size() > users.size())
.switchIfEmpty(Mono.error(
new IllegalStateException("Cannot remove all instance administrators")))
.then(Mono.defer(() -> {
// TODO : Replace cloud hosting check with a feature flag check for multi tenancy
boolean cloudHosting = commonConfig.isCloudHosting();
Mono<PermissionGroup> organizationAdminPgMono = Mono.just(new PermissionGroup());
if (!cloudHosting) {
organizationAdminPgMono = getDefaultOrganizationAdminPermissionGroup();
}
return Mono.zip(getInstanceAdminPermissionGroup(), organizationAdminPgMono)
.flatMap(tuple -> {
PermissionGroup instanceAdminPg = tuple.getT1();
PermissionGroup organizationAdminPg = tuple.getT2();
if (instanceAdminPg.getAssignedToUserIds() == null) {
instanceAdminPg.setAssignedToUserIds(new HashSet<>());
}
Set<String> assignedToUserIds = new HashSet<>(instanceAdminPg.getAssignedToUserIds());
assignedToUserIds.removeAll(users.stream().map(User::getId).collect(Collectors.toList()));
BridgeUpdate updateObj = Bridge.update();
String path = PermissionGroup.Fields.assignedToUserIds;
updateObj.set(path, assignedToUserIds);
// Make Instance Admin is called before the first administrator is created.
Mono<Integer> updateInstanceAdminPgAssignmentMono =
permissionGroupRepository.updateById(instanceAdminPg.getId(), updateObj);
if (!hasLength(organizationAdminPg.getId())) {
return updateInstanceAdminPgAssignmentMono;
}
// Also unassign the users from the organization admin permission group
Set<String> organizationAdminAssignedToUserIds = new HashSet<>();
if (organizationAdminPg.getAssignedToUserIds() != null) {
organizationAdminAssignedToUserIds.addAll(organizationAdminPg.getAssignedToUserIds());
}
organizationAdminAssignedToUserIds.removeAll(
users.stream().map(User::getId).collect(Collectors.toList()));
BridgeUpdate updateObj2 = Bridge.update();
String path2 = PermissionGroup.Fields.assignedToUserIds;
updateObj2.set(path2, organizationAdminAssignedToUserIds);
return updateInstanceAdminPgAssignmentMono.then(
permissionGroupRepository.updateById(organizationAdminPg.getId(), updateObj2));
})
.then(Mono.just(users))
.flatMapMany(Flux::fromIterable)
.flatMap(user -> permissionGroupRepository.evictAllPermissionGroupCachesForUser(
user.getEmail(), user.getOrganizationId()))
.then(Mono.just(Boolean.TRUE));
}));
}

Copy link
Contributor

@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

🧹 Nitpick comments (1)
app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration10000_UpdateSuperUser.java (1)

113-119: Consider using bulk update for better performance.

Since both updates are setting the same user IDs, consider combining them into a single bulk operation.

Example refactor:

-// Assign the users to the instance admin pg
-Update update = new Update().set(PermissionGroup.Fields.assignedToUserIds, userIds);
-mongoTemplate.updateFirst(instanceAdminPgQuery, update, PermissionGroup.class);
-
-// Assign the users to the default organization admin pg
-Update orgAdminUpdate = new Update().set(PermissionGroup.Fields.assignedToUserIds, userIds);
-mongoTemplate.updateFirst(orgAdminPermissionGroupQuery, orgAdminUpdate, PermissionGroup.class);
+// Bulk update both permission groups
+Query combinedQuery = new Query().addCriteria(
+    new Criteria().orOperator(
+        where(PermissionGroup.Fields.id).is(instanceAdminPermissionGroupId),
+        where(PermissionGroup.Fields.defaultDomainType).is(Organization.class.getSimpleName())
+            .and(PermissionGroup.Fields.defaultDomainId).is(organization.getId())
+    )
+);
+Update bulkUpdate = new Update().set(PermissionGroup.Fields.assignedToUserIds, userIds);
+mongoTemplate.updateMulti(combinedQuery, bulkUpdate, PermissionGroup.class);
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d99137c and c0c9f98.

📒 Files selected for processing (1)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration10000_UpdateSuperUser.java (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: perform-test / server-build / server-unit-tests
  • GitHub Check: server-unit-tests / server-unit-tests
  • GitHub Check: server-spotless / spotless-check
🔇 Additional comments (2)
app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/db/ce/Migration10000_UpdateSuperUser.java (2)

73-78: LGTM! Clear and descriptive query variable renaming.

The renaming from permissionGroupQuery to instanceAdminPgQuery improves code clarity by explicitly indicating its purpose.


84-90: Consider adding null check for organization.

The organization object is used to build the org admin query without verifying its existence.

Add a null check before proceeding:

 Organization organization = mongoTemplate.findOne(organizationQuery, Organization.class);
+if (organization == null) {
+    log.error("Default organization not found. Skipping org admin permission group update.");
+    return;
+}

@trishaanand trishaanand removed the ok-to-test Required label for CI label Feb 24, 2025
Copy link

Failed server tests

  • com.appsmith.server.helpers.UserUtilsTest#makeInstanceAdministrator_WhenUserAlreadyAdmin_MaintainsPermissionsSuccessfully
  • com.appsmith.server.helpers.UserUtilsTest#makeInstanceAdministrator_WhenUsersProvided_AssignsPermissionsSuccessfully
  • com.appsmith.server.helpers.UserUtilsTest#removeInstanceAdmin_WhenUsersProvided_RemovesPermissionsSuccessfully
  • com.appsmith.server.repositories.CacheableRepositoryTest#getUserPermissionsTest_onPermissionGroupDelete_valid
  • com.appsmith.server.services.PermissionGroupServiceTest#invalid_leaveRole
  • com.appsmith.server.services.ce.OrganizationServiceCETest#checkAndExecuteMigrationsForOrganizationFeatureFlags_emptyMigrationMap_revertSameOrganization
  • com.appsmith.server.services.ce.OrganizationServiceCETest#checkAndExecuteMigrationsForOrganizationFeatureFlags_withPendingMigration_exceptionWhileRunningMigration_getUpdatedOrganization
  • com.appsmith.server.services.ce.OrganizationServiceCETest#checkAndExecuteMigrationsForOrganizationFeatureFlags_withPendingMigration_getUpdatedOrganization
  • com.appsmith.server.services.ce.OrganizationServiceCETest#ensureMapsKey
  • com.appsmith.server.services.ce.OrganizationServiceCETest#getOrganizationConfig_Valid_AnonymousUser
  • com.appsmith.server.services.ce.OrganizationServiceCETest#setEmailVerificationEnabledFalseAndGetItBack
  • com.appsmith.server.services.ce.OrganizationServiceCETest#setEmailVerificationEnabled_WithInvalidSMTPHost_ReturnsError
  • com.appsmith.server.services.ce.OrganizationServiceCETest#setEmailVerificationEnabled_WithValidSMTPHost_Success
  • com.appsmith.server.services.ce.OrganizationServiceCETest#setMapsKeyAndGetItBack
  • com.appsmith.server.services.ce.OrganizationServiceCETest#setMapsKeyWithoutAuthentication
  • com.appsmith.server.services.ce.OrganizationServiceCETest#setMapsKeyWithoutAuthorization
  • com.appsmith.server.services.ce.OrganizationServiceCETest#testDeserializationErrors
  • com.appsmith.server.services.ce.OrganizationServiceCETest#updateOrganizationConfiguration_updateStrongPasswordPolicy_success

1 similar comment
Copy link

Failed server tests

  • com.appsmith.server.helpers.UserUtilsTest#makeInstanceAdministrator_WhenUserAlreadyAdmin_MaintainsPermissionsSuccessfully
  • com.appsmith.server.helpers.UserUtilsTest#makeInstanceAdministrator_WhenUsersProvided_AssignsPermissionsSuccessfully
  • com.appsmith.server.helpers.UserUtilsTest#removeInstanceAdmin_WhenUsersProvided_RemovesPermissionsSuccessfully
  • com.appsmith.server.repositories.CacheableRepositoryTest#getUserPermissionsTest_onPermissionGroupDelete_valid
  • com.appsmith.server.services.PermissionGroupServiceTest#invalid_leaveRole
  • com.appsmith.server.services.ce.OrganizationServiceCETest#checkAndExecuteMigrationsForOrganizationFeatureFlags_emptyMigrationMap_revertSameOrganization
  • com.appsmith.server.services.ce.OrganizationServiceCETest#checkAndExecuteMigrationsForOrganizationFeatureFlags_withPendingMigration_exceptionWhileRunningMigration_getUpdatedOrganization
  • com.appsmith.server.services.ce.OrganizationServiceCETest#checkAndExecuteMigrationsForOrganizationFeatureFlags_withPendingMigration_getUpdatedOrganization
  • com.appsmith.server.services.ce.OrganizationServiceCETest#ensureMapsKey
  • com.appsmith.server.services.ce.OrganizationServiceCETest#getOrganizationConfig_Valid_AnonymousUser
  • com.appsmith.server.services.ce.OrganizationServiceCETest#setEmailVerificationEnabledFalseAndGetItBack
  • com.appsmith.server.services.ce.OrganizationServiceCETest#setEmailVerificationEnabled_WithInvalidSMTPHost_ReturnsError
  • com.appsmith.server.services.ce.OrganizationServiceCETest#setEmailVerificationEnabled_WithValidSMTPHost_Success
  • com.appsmith.server.services.ce.OrganizationServiceCETest#setMapsKeyAndGetItBack
  • com.appsmith.server.services.ce.OrganizationServiceCETest#setMapsKeyWithoutAuthentication
  • com.appsmith.server.services.ce.OrganizationServiceCETest#setMapsKeyWithoutAuthorization
  • com.appsmith.server.services.ce.OrganizationServiceCETest#testDeserializationErrors
  • com.appsmith.server.services.ce.OrganizationServiceCETest#updateOrganizationConfiguration_updateStrongPasswordPolicy_success

@trishaanand trishaanand added the ok-to-test Required label for CI label Feb 25, 2025
Copy link
Contributor

@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)
app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/UserUtilsCE.java (1)

125-176: ⚠️ Potential issue

Add safeguard against removing all instance administrators.

Similar to the makeInstanceAdministrator method, we should prevent removing all instance administrators.

 public Mono<Boolean> removeInstanceAdmin(List<User> users) {
+    return getInstanceAdminPermissionGroup()
+        .filter(pg -> pg.getAssignedToUserIds() != null && 
+            pg.getAssignedToUserIds().size() > users.size())
+        .switchIfEmpty(Mono.error(
+            new AppsmithException(AppsmithError.REMOVE_LAST_INSTANCE_ADMIN_ERROR)))
+        .flatMap(permissionGroup -> {
             // TODO : Replace cloud hosting check with a feature flag check for multi tenancy
             boolean cloudHosting = commonConfig.isCloudHosting();
             Mono<PermissionGroup> organizationAdminPgMono = Mono.just(new PermissionGroup());
             // Rest of the method...
+        });
 }
🧹 Nitpick comments (1)
app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/UserUtilsCE.java (1)

72-123: Implementation looks good but consider adding a cache invalidation strategy.

The method effectively handles both instance and organization admin roles, but consider invalidating relevant caches to ensure consistency when permission assignments change.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c0c9f98 and d4f1f97.

📒 Files selected for processing (2)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/UserUtils.java (1 hunks)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/UserUtilsCE.java (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/UserUtils.java
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: perform-test / client-build / client-build
  • GitHub Check: perform-test / rts-build / build
  • GitHub Check: perform-test / server-build / server-unit-tests
🔇 Additional comments (3)
app/server/appsmith-server/src/main/java/com/appsmith/server/helpers/ce/UserUtilsCE.java (3)

4-8: Dependencies and injection look good.

The added dependencies and constructor modifications properly support the refactoring for instance administration functionality.

Also applies to: 37-39, 45-53


178-193: Good use of caching for performance optimization.

The caching implementation for instance admin permission group improves performance by avoiding repeated database queries.


195-208: Good implementation for retrieving the organization admin permission group.

The method effectively uses caching and handles the organization context appropriately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement New feature or request ok-to-test Required label for CI
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant