Skip to content

feat: enable PBAC checking on organization settings page #22467

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

Merged
merged 31 commits into from
Jul 31, 2025

Conversation

sean-brydon
Copy link
Member

@sean-brydon sean-brydon commented Jul 14, 2025

Overview

This PR implements comprehensive Permission-Based Access Control (PBAC) for organization
settings pages, replacing basic role-based authorization with a more granular permission
system. The implementation includes proper fallback mechanisms for when PBAC is disabled
via feature flags.

Changes Made

🔧 Core Infrastructure

  • Added getResourcePermissions utility (packages/features/pbac/lib/resource-permissions.ts)
  • Central function for checking permissions with fallback support
  • Created comprehensive test suite
    (packages/features/pbac/lib/tests/resource-permissions.test.ts) - 283 lines of tests
    covering all scenarios
  • Added Resource.Attributes enum to permission registry for attributes management
  • Database migration for PBAC attributes permissions

🎯 Frontend Permission Integration

Updated all organization settings pages to use PBAC:

Pages Updated:

  • /settings/organizations/attributes - Attributes management
  • /settings/organizations/profile - Organization profile
  • /settings/organizations/general - General settings
  • /settings/organizations/privacy - Privacy settings
  • /settings/organizations/sso - SSO configuration
  • /settings/organizations/dsync - Directory sync
  • /settings/my-account/general - User account settings

Permission Matrix

When PBAC is ENABLED (Feature Flag ON)

Permissions are determined by the user's assigned roles and the organization's PBAC
configuration.

When PBAC is DISABLED (Feature Flag OFF - Fallback Mode)

Page/Feature MEMBER ADMIN OWNER Notes
Attributes
- View attributes All members can view
- Create attributes Admin+ required
- Edit attributes Admin+ required
- Delete attributes Admin+ required
- Toggle active/inactive Admin+ required
Organization Profile 🟧 Admin+ can update
Organization General 🟧 Admin+ can update
Organization Privacy 🟧 Admin+ can update
SSO Configuration Admin+ can edit
Directory Sync Admin+ can edit
Team Invites 🟧 Admin+ can invite
Organization Invites 🟧 Admin+ can invite

🟧 - Users have some access to reads.

How to Test

🧪 Testing PBAC Enabled

INSERT INTO "TeamFeatures" ("teamId", "featureId", "assignedBy", "assignedAt", "updatedAt") VALUES (7, 'pbac', 'WHOAREYOU', '2025-07-14 09:03:52.825', '2025-07-14 09:03:52.825');

  1. Enable PBAC feature flag for your organization
  2. Create custom roles with specific permissions via the roles management interface
  3. Assign users to roles with limited permissions
  4. Test each page to ensure users only see actions they have permissions for
  5. Verify API endpoints reject requests for unauthorized actions

🧪 Testing PBAC Disabled (Fallback)

  1. Disable PBAC feature flag for your organization
  2. Test with different membership roles (MEMBER, ADMIN, OWNER)
  3. Verify fallback behavior matches the permission matrix above
  4. Ensure members cannot access admin functions
  5. Confirm admins and owners have full access

@sean-brydon sean-brydon requested a review from a team as a code owner July 14, 2025 08:51
@graphite-app graphite-app bot requested review from a team July 14, 2025 08:52
@github-actions github-actions bot added the ❗️ migrations contains migration files label Jul 14, 2025
@keithwillcode keithwillcode added consumer core area: core, team members only labels Jul 14, 2025
@dosubot dosubot bot added enterprise area: enterprise, audit log, organisation, SAML, SSO ✨ feature New feature or request labels Jul 14, 2025
Copy link

delve-auditor bot commented Jul 14, 2025

No security or compliance issues detected. Reviewed everything up to 3b03b59.

Security Overview
  • 🔎 Scanned files: 26 changed file(s)
Detected Code Changes
Change Type Relevant files
Enhancement ► organization settings/attributes/page.tsx
    Add role-based permissions for attributes pages
► organizations/privacy/page.tsx
    Add permissions controls
► organizations/general/page.tsx
    Implement organization permissions
► organizations/profile/page.tsx
    Add role-based access control
Feature ► resource-permissions.ts
    Implement resource permissions handler
► permission-registry.ts
    Add attribute permissions to registry
Database ► migration.sql
    Add attributes PBAC permissions
Refactor ► organizations/update.handler.ts
    Update organization permission checks
► attributes handlers
    Add permission checks to CRUD operations

Reply to this PR with @delve-auditor followed by a description of what change you want and we'll auto-submit a change to this PR to implement it.

Copy link

graphite-app bot commented Jul 14, 2025

Graphite Automations

"Add consumer team as reviewer" took an action on this PR • (07/14/25)

1 reviewer was added to this PR based on Keith Williams's automation.

"Add foundation team as reviewer" took an action on this PR • (07/14/25)

1 reviewer was added to this PR based on Keith Williams's automation.

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

cubic found 2 issues across 25 files. Review them in cubic.dev

React with 👍 or 👎 to teach cubic. Tag @cubic-dev-ai to give specific feedback.

Copy link

vercel bot commented Jul 14, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

2 Skipped Deployments
Name Status Preview Comments Updated (UTC)
cal ⬜️ Ignored (Inspect) Visit Preview Jul 30, 2025 10:06am
cal-eu ⬜️ Ignored (Inspect) Visit Preview Jul 30, 2025 10:06am

Copy link
Contributor

coderabbitai bot commented Jul 15, 2025

Walkthrough

This change introduces comprehensive, resource-based permission enforcement for organization-related settings and attribute management across the application. It implements a new getResourcePermissions utility, integrating it into both server-side page handlers and API endpoints to determine user capabilities (read, edit, create, delete) based on roles, fallback mappings, and feature flags. The UI components for organization settings and attributes are refactored to receive and act upon these granular permissions, replacing prior admin/owner checks. The PBAC (policy-based access control) system is extended to support attribute permissions, including database migrations, registry updates, and localization. Extensive tests are added for the new permission logic.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • fix: (PBAC) Add organization fallback on resources  #22733: Refactors the internal logic of PermissionCheckService to aggregate permissions from both team-level and organization-level roles, directly impacting the accuracy of permission checks now enforced in this PR.
  • feat: (PBAC) Add db seed  #22704: Adds a database seed script for PBAC-enabled organizations, providing foundational data that supports the permission enforcement mechanisms introduced in this PR.

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/org-settings-pbac

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
🪧 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @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.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

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.

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: 3

♻️ Duplicate comments (1)
packages/prisma/migrations/20250714075355_add_attributes_pbac_permissions/migration.sql (1)

4-4: Use timezone-aware NOW() function for consistent timestamps.

Direct usage of NOW() without timezone specification can lead to inconsistent timestamp data across different database environments.

Apply this diff to ensure UTC consistency:

-  gen_random_uuid(), 'admin_role', resource, action, NOW()
+  gen_random_uuid(), 'admin_role', resource, action, NOW() AT TIME ZONE 'UTC'
-  gen_random_uuid(), 'member_role', resource, action, NOW()
+  gen_random_uuid(), 'member_role', resource, action, NOW() AT TIME ZONE 'UTC'

Also applies to: 17-17

🧹 Nitpick comments (6)
packages/trpc/server/routers/viewer/attributes/toggleActive.handler.ts (2)

20-28: Consider optimizing the database query for membership role.

The current implementation performs a separate database query to fetch the user's membership role, but this information might already be available in the user context or could be fetched more efficiently.

Consider if the membership role is already available in ctx.user.organization or if it can be included in the initial user context to avoid the additional database query:

-  const { role: userOrgRole } = await prisma.membership.findFirst({
-    where: {
-      userId: ctx.user.id,
-      organizationId: org.id,
-    },
-    select: {
-      role: true,
-    },
-  });
+  const userOrgRole = org.role || await prisma.membership.findFirst({
+    where: {
+      userId: ctx.user.id,
+      organizationId: org.id,
+    },
+    select: {
+      role: true,
+    },
+  }).then(result => result?.role);

30-35: Improve error message clarity and consistency.

The error message "You need to be apart of an organization to use this feature" contains a typo and could be more specific about what's missing.

Apply this diff to improve the error message:

-      message: "You need to be apart of an organization to use this feature",
+      message: "You need to be a part of an organization to use this feature",
apps/web/public/static/locales/en/common.json (2)

283-286: Avoid overly-generic translation keys ("confirmation")

A single, top-level key named "confirmation" is extremely generic and is likely to collide with existing or future keys.
Consider renaming it to something more specific, e.g. "booking_confirmation_title" or "confirmation_label", to keep the namespace clear and avoid accidental overwrites during i18n merges.


1837-1839: Keep admin-only helper keys consistent

These three new helper messages follow the pattern of earlier ones (only_admin_can_manage_sso_org, etc.) – 👍.
Minor: for readability and translator context, consider adding a trailing period or rewriting to second-person (“Only organization admins or owners can manage …”).
Not blocking.

apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/sso/page.tsx (1)

31-41: Consider checking read permissions as well.

The current implementation only checks canEdit permission. For consistency and better access control, consider also checking canRead permission to ensure users can at least view the SSO configuration before attempting to edit.

-  const { canEdit } = await getResourcePermissions({
+  const { canRead, canEdit } = await getResourcePermissions({
     userId: session.user.id,
     teamId: session.user.profile.organizationId,
     resource: Resource.Organization,
     userRole: session.user.org.role,
     fallbackRoles: {
+      read: {
+        roles: [MembershipRole.MEMBER, MembershipRole.ADMIN, MembershipRole.OWNER],
+      },
       update: {
         roles: [MembershipRole.ADMIN, MembershipRole.OWNER],
       },
     },
   });

And update the component prop:

-      <OrgSSOView permissions={{ canEdit }} />
+      <OrgSSOView permissions={{ canRead, canEdit }} />
apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/dsync/page.tsx (1)

31-41: Consider adding read permission check for consistency.

Similar to the SSO page, this only checks canEdit. Consider adding canRead permission check for consistency with the general page implementation.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 35022a0 and 799743e.

📒 Files selected for processing (27)
  • apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/attributes/page.tsx (2 hunks)
  • apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/dsync/page.tsx (2 hunks)
  • apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/privacy/page.tsx (2 hunks)
  • apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/sso/page.tsx (2 hunks)
  • apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/general/page.tsx (2 hunks)
  • apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/profile/page.tsx (2 hunks)
  • apps/web/public/static/locales/en/common.json (4 hunks)
  • packages/features/ee/dsync/page/team-dsync-view.tsx (2 hunks)
  • packages/features/ee/organizations/pages/components/DisablePhoneOnlySMSNotificationsSwitch.tsx (1 hunks)
  • packages/features/ee/organizations/pages/components/LockEventTypeSwitch.tsx (3 hunks)
  • packages/features/ee/organizations/pages/components/NoSlotsNotificationSwitch.tsx (1 hunks)
  • packages/features/ee/organizations/pages/settings/attributes/attributes-list-view.tsx (4 hunks)
  • packages/features/ee/organizations/pages/settings/general.tsx (5 hunks)
  • packages/features/ee/organizations/pages/settings/privacy.tsx (1 hunks)
  • packages/features/ee/organizations/pages/settings/profile.tsx (2 hunks)
  • packages/features/ee/sso/page/orgs-sso-view.tsx (1 hunks)
  • packages/features/pbac/domain/mappers/PermissionMapper.ts (1 hunks)
  • packages/features/pbac/domain/types/permission-registry.ts (2 hunks)
  • packages/features/pbac/infrastructure/mappers/RoleOutputMapper.ts (2 hunks)
  • packages/features/pbac/lib/__tests__/resource-permissions.test.ts (1 hunks)
  • packages/features/pbac/lib/resource-permissions.ts (1 hunks)
  • packages/prisma/migrations/20250714075355_add_attributes_pbac_permissions/migration.sql (1 hunks)
  • packages/trpc/server/routers/viewer/attributes/create.handler.ts (2 hunks)
  • packages/trpc/server/routers/viewer/attributes/delete.handler.ts (2 hunks)
  • packages/trpc/server/routers/viewer/attributes/edit.handler.ts (2 hunks)
  • packages/trpc/server/routers/viewer/attributes/toggleActive.handler.ts (2 hunks)
  • packages/trpc/server/routers/viewer/organizations/update.handler.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
apps/web/public/static/locales/en/common.json (1)

undefined

<retrieved_learning>
Learnt from: bandhan-majumder
PR: #22359
File: packages/lib/server/locales/en/common.json:1336-1339
Timestamp: 2025-07-14T16:31:45.201Z
Learning: When making localization changes for new features, it's often safer to add new strings rather than modify existing ones to avoid breaking existing functionality that depends on the original strings. This approach allows for feature-specific customization while maintaining backward compatibility.
</retrieved_learning>

🧬 Code Graph Analysis (5)
packages/trpc/server/routers/viewer/attributes/edit.handler.ts (1)
packages/platform/libraries/index.ts (2)
  • TRPCError (56-56)
  • MembershipRole (98-98)
apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/attributes/page.tsx (2)
packages/features/auth/lib/getServerSession.ts (1)
  • getServerSession (32-135)
packages/platform/libraries/index.ts (1)
  • MembershipRole (98-98)
packages/features/pbac/lib/resource-permissions.ts (4)
packages/platform/libraries/index.ts (1)
  • MembershipRole (98-98)
packages/features/flags/features.repository.ts (1)
  • FeaturesRepository (18-252)
packages/features/pbac/services/permission-check.service.ts (1)
  • PermissionCheckService (13-264)
packages/features/pbac/domain/mappers/PermissionMapper.ts (1)
  • PermissionMapper (7-120)
packages/trpc/server/routers/viewer/organizations/update.handler.ts (2)
packages/platform/libraries/index.ts (2)
  • TRPCError (56-56)
  • MembershipRole (98-98)
scripts/prepare-local-for-delegation-credentials-testing.js (1)
  • org (42-44)
packages/features/ee/organizations/pages/settings/attributes/attributes-list-view.tsx (1)
packages/ui/components/dropdown/Dropdown.tsx (5)
  • Dropdown (12-12)
  • DropdownMenuTrigger (15-26)
  • DropdownMenuContent (34-51)
  • DropdownMenuItem (63-71)
  • DropdownItem (161-181)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Install dependencies / Yarn install & cache
  • GitHub Check: Security Check
🔇 Additional comments (40)
packages/features/pbac/infrastructure/mappers/RoleOutputMapper.ts (1)

14-14: Good refactor for static method clarity.

Using explicit class name references (RoleOutputMapper.toDomainPermission and RoleOutputMapper.toDomain) instead of this for static methods improves code readability and follows static method best practices.

Also applies to: 31-31

packages/trpc/server/routers/viewer/attributes/edit.handler.ts (1)

1-2: Excellent implementation of PBAC permission checking.

The permission checking logic is well-structured:

  • Proper membership role validation
  • Appropriate use of getResourcePermissions utility
  • Clear fallback roles for backward compatibility
  • Specific error messages for unauthorized access

This implementation aligns perfectly with the PBAC framework described in the PR objectives.

Also applies to: 5-5, 23-57

packages/trpc/server/routers/viewer/attributes/delete.handler.ts (1)

1-2: Consistent and secure permission implementation.

The delete handler properly implements the PBAC pattern with:

  • Consistent permission checking flow matching other handlers
  • Appropriate fallback roles for delete operations (ADMIN, OWNER)
  • Proper error handling and user feedback

The implementation maintains security while providing clear authorization feedback.

Also applies to: 4-4, 21-55

packages/features/ee/organizations/pages/components/NoSlotsNotificationSwitch.tsx (1)

9-11: Good architectural improvement for centralized permission handling.

Removing the component-level isAdminOrOwner check aligns with the broader PBAC refactor. This centralizes permission logic at higher levels and allows for more granular permission control through the new permission system.

Also applies to: 13-13

packages/trpc/server/routers/viewer/attributes/toggleActive.handler.ts (1)

37-54: PBAC permission check implementation looks solid.

The permission checking logic correctly uses the getResourcePermissions utility with appropriate fallback roles for admin and owner users. The error handling is appropriate and the permission check aligns with the PR objectives.

packages/features/ee/organizations/pages/settings/privacy.tsx (2)

7-7: Good refactoring to externalize permission logic.

The component now properly separates concerns by accepting permissions as props instead of checking roles internally. This aligns well with the centralized PBAC approach.


11-11: Verify the permission logic combines correctly with invite status.

The disabled state logic correctly combines both permission checking and invite status. This ensures that users without edit permissions OR users with pending invites cannot modify the privacy settings.

apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/privacy/page.tsx (3)

26-30: Verify session validation covers all edge cases.

The session validation checks for user ID, organization ID, and organization data. This looks comprehensive for preventing unauthorized access.


32-45: Permission configuration aligns with expected access patterns.

The fallback roles configuration correctly implements the expected access pattern:

  • All members (MEMBER, ADMIN, OWNER) can read organization privacy settings
  • Only admins and owners can update privacy settings

This matches the typical organization permission hierarchy.


47-49: Appropriate redirect for unauthorized users.

The redirect to /settings/profile for users without read permissions is appropriate and consistent with the pattern used for missing session data.

packages/features/ee/organizations/pages/components/DisablePhoneOnlySMSNotificationsSwitch.tsx (1)

15-15: Confirmed parent permission guard in general settings

The DisablePhoneOnlySMSNotificationsSwitch is wrapped in a permissions.canEdit && (…) check in
packages/features/ee/organizations/pages/settings/general.tsx, ensuring it only renders for authorized users. No further changes needed.

packages/features/ee/dsync/page/team-dsync-view.tsx (3)

14-18: Good defensive programming with optional permissions prop.

The permissions prop is correctly defined as optional, which provides flexibility for different usage contexts while maintaining type safety.


45-53: Excellent user experience for unauthorized access.

The implementation handles unauthorized access gracefully by showing a clear message instead of hiding the component entirely. This provides better user experience by explaining why the feature is not available.


45-45: Secure default for permission checking.

Using permissions?.canEdit ?? false ensures that the component defaults to the most restrictive state when permissions are not provided, which is a good security practice.

packages/features/pbac/domain/mappers/PermissionMapper.ts (1)

49-52: LGTM! Smart improvement to support hierarchical resource names.

The change from left-to-right splitting to right-to-left splitting elegantly handles resource names with dots (like "organization.attributes") while maintaining backward compatibility. The implementation correctly identifies the action as the substring after the last dot, ensuring proper parsing of hierarchical resource structures.

The existing validation logic ensures robustness, and the added comment clearly explains the intent.

apps/web/public/static/locales/en/common.json (2)

868-872: No duplicate i18n keys found for OAuth clients

I’ve searched for each of the four keys and confirmed they each appear only once in common.json—there are no exact duplicates elsewhere in the file. It’s safe to merge.

• "oauth_clients" (at line 868)
• "oauth_clients_description" (at line 869)
• "create_oauth_client" (at line 870)
• "create_oauth_client_description" (at line 871)


3293-3299: No duplicate PBAC permission keys detected

I’ve verified that each of the new pbac_desc_manage_roles and pbac_desc_*_organization_attributes entries appears exactly once in apps/web/public/static/locales/en/common.json. There are no duplicate keys, so no action is needed.

apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/profile/page.tsx (2)

31-47: LGTM! Permission fetching logic is well-structured.

The permission fetching implementation follows best practices:

  • Uses proper resource type (Resource.Organization)
  • Includes appropriate user context (userId, teamId, userRole)
  • Defines sensible fallback roles for organization management
  • Fetches all necessary CRUD permissions

54-60: LGTM! Props passing is consistent with the new permission model.

The permissions are properly passed to the LegacyPage component, enabling permission-aware rendering throughout the organization profile functionality.

packages/trpc/server/routers/viewer/attributes/create.handler.ts (2)

26-41: LGTM! Membership role query is efficient and correct.

The membership role query properly:

  • Targets the correct user-organization relationship
  • Uses select to fetch only the required role field
  • Includes proper validation for organization membership

43-60: LGTM! Permission checking implementation is comprehensive.

The permission checking logic is well-implemented:

  • Uses the correct resource type (Resource.Attributes)
  • Includes proper user context and role information
  • Defines appropriate fallback roles for create operations
  • Provides clear error messages for unauthorized access
packages/features/ee/organizations/pages/settings/profile.tsx (2)

78-86: LGTM! Props interface is well-defined.

The permissions prop interface properly defines the expected permission structure with optional boolean flags for read, edit, and delete operations.


136-177: LGTM! Conditional rendering logic is appropriate.

The conditional rendering properly:

  • Shows editable form and appearance settings when canEdit is true
  • Displays read-only view with essential information when canEdit is false
  • Includes functionality to copy organization link in read-only mode
  • Maintains good user experience across permission levels
packages/features/pbac/domain/types/permission-registry.ts (2)

6-6: LGTM! Resource enum addition follows naming conventions.

The new Attributes resource with value "organization.attributes" follows the established pattern and clearly indicates its scope within the organization context.


279-307: LGTM! Permission registry entry is comprehensive and well-structured.

The attributes permission registry entry properly includes:

  • Resource internationalization key
  • All CRUD operations (Read, Update, Delete, Create)
  • Consistent categorization as "attributes"
  • Proper internationalization keys for actions and descriptions
  • Descriptive permission details
packages/features/ee/sso/page/orgs-sso-view.tsx (3)

10-15: LGTM! Props interface is properly defined.

The OrgSSOViewProps interface correctly defines the expected permissions structure with an optional canEdit boolean flag.


22-22: LGTM! Session loading state fix is necessary.

The fix properly returns the SkeletonLoader component during session loading, preventing potential rendering issues.


28-39: LGTM! Conditional rendering logic is appropriate.

The conditional rendering properly:

  • Extracts canEdit from permissions with sensible default
  • Shows SSO configuration when user has edit permissions
  • Displays informative message when editing is not allowed
  • Maintains good user experience across permission levels
apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/general/page.tsx (2)

28-30: Verify redirect destination consistency.

This page redirects to /settings/profile while the SSO and dsync pages redirect to /settings/organizations/general. Please verify if this difference is intentional.


32-49: Well-implemented permission checks!

Good implementation with both read and edit permissions properly configured with appropriate fallback roles for all membership levels.

packages/features/ee/organizations/pages/components/LockEventTypeSwitch.tsx (1)

21-29: LGTM! Aligns with the new permission model.

The removal of isAdminOrOwner prop is consistent with the migration to permission-based access control. The parent component now handles permission checks.

packages/features/pbac/lib/resource-permissions.ts (2)

78-85: Good defensive programming with nullish coalescing.

The use of ?? false ensures that undefined permissions default to false, which is the secure default.


71-77: Error handling confirmed for getResourcePermissions

The getResourcePermissions method is already wrapped in a try–catch that logs errors via this.logger.error(error) and returns an empty array on failure, satisfying the original request.

• File: packages/features/pbac/services/permission-check.service.ts
• Method: getResourcePermissions (lines ~70–95)

No further changes required.

apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/(org-admin-only)/attributes/page.tsx (1)

23-61: Well-implemented server-side permission checks!

The implementation properly validates the session, retrieves permissions with appropriate fallback roles, and passes only the necessary permissions to the child component. The redirect logic for unauthorized access is also correctly implemented.

packages/trpc/server/routers/viewer/organizations/update.handler.ts (1)

127-140: Good implementation of resource-based permissions

The permission check implementation correctly uses the new getResourcePermissions function with appropriate fallback roles, ensuring only owners and admins can update organization settings.

packages/features/ee/organizations/pages/settings/attributes/attributes-list-view.tsx (2)

36-133: Excellent implementation of permission-based UI controls

The conditional rendering of UI elements based on permissions is well-implemented. The toggle switch, dropdown menu, and individual actions are properly gated, ensuring users only see actions they're authorized to perform.


135-213: Consistent permission handling throughout the component

The OrganizationAttributesPage component properly accepts and propagates permissions, with create buttons appropriately gated in both list and empty states.

packages/features/ee/organizations/pages/settings/general.tsx (2)

42-99: Clean refactoring to permission-based access control

The migration from isAdminOrOwner to a granular permissions object is well-executed. Getting the locale directly from the session is more efficient than a separate query.


151-233: Proper permission gating in form controls

The form's disabled state and submit button visibility are correctly controlled by permissions.canEdit, ensuring consistent behavior throughout the component.

packages/features/pbac/lib/__tests__/resource-permissions.test.ts (1)

1-278: Comprehensive test coverage for permission system

Excellent test implementation with thorough coverage of:

  • PBAC disabled scenarios with fallback roles
  • PBAC enabled scenarios with permission service
  • Conditional role access
  • Edge cases and error handling

The test structure is clean, well-organized, and properly uses mocks for isolation.

Comment on lines 24 to 29
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
const t = await getTranslate();

if (!session?.user.id || !session?.user.profile?.organizationId || !session?.user.org) {
return redirect("/settings/profile");
}
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

Fix session validation logic.

The session validation check has a logical issue. The redirect happens when the user lacks a valid session, but the check should also ensure the user is properly authenticated and has organization access.

- if (!session?.user.id || !session?.user.profile?.organizationId || !session?.user.org) {
+ if (!session?.user?.id || !session?.user?.profile?.organizationId || !session?.user?.org) {
    return redirect("/settings/profile");
  }

The optional chaining on session?.user.id should be session?.user?.id to properly handle cases where session.user might be undefined.

📝 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
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
const t = await getTranslate();
if (!session?.user.id || !session?.user.profile?.organizationId || !session?.user.org) {
return redirect("/settings/profile");
}
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
const t = await getTranslate();
if (!session?.user?.id || !session?.user?.profile?.organizationId || !session?.user.org) {
return redirect("/settings/profile");
}
🤖 Prompt for AI Agents
In
apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/profile/page.tsx
around lines 24 to 29, update the session validation logic to correctly check
for user authentication and organization access. Change the optional chaining
from session?.user.id to session?.user?.id to safely handle cases where
session.user might be undefined. Ensure the condition verifies that session,
session.user, session.user.id, session.user.profile.organizationId, and
session.user.org all exist before proceeding; otherwise, redirect to
"/settings/profile".

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

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 18ad08e and 365a320.

📒 Files selected for processing (2)
  • apps/web/public/static/locales/en/common.json (3 hunks)
  • packages/features/pbac/domain/types/permission-registry.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/features/pbac/domain/types/permission-registry.ts
🧰 Additional context used
🧠 Learnings (1)
apps/web/public/static/locales/en/common.json (2)

Learnt from: CR
PR: calcom/cal.com#0
File: .cursor/rules/review.mdc:0-0
Timestamp: 2025-07-28T11:50:23.917Z
Learning: Applies to docs/api-reference/v2/openapi.json : When docs changes are made in /docs/api-reference/v2/openapi.json, ensure 'summary' fields are short, concise, do not end with a period, and are written in proper American English

Learnt from: bandhan-majumder
PR: #22359
File: packages/lib/server/locales/en/common.json:1336-1339
Timestamp: 2025-07-14T16:31:45.233Z
Learning: When making localization changes for new features, it's often safer to add new strings rather than modify existing ones to avoid breaking existing functionality that depends on the original strings. This approach allows for feature-specific customization while maintaining backward compatibility.

🪛 Biome (2.1.2)
apps/web/public/static/locales/en/common.json

[error] 3311-3311: The key pbac_desc_manage_roles was already declared.

This where a duplicated key was declared again.

If a key is defined multiple times, only the last definition takes effect. Previous definitions are ignored.

(lint/suspicious/noDuplicateObjectKeys)

Copy link
Contributor

github-actions bot commented Jul 29, 2025

E2E results are ready!

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

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 51c0a47 and 8e2df35.

📒 Files selected for processing (2)
  • apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/profile/page.tsx (2 hunks)
  • packages/features/ee/dsync/page/team-dsync-view.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/app/(use-page-wrapper)/settings/(settings-layout)/organizations/profile/page.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.tsx

📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)

Always use t() for text localization in frontend code; direct text embedding should trigger a warning

Files:

  • packages/features/ee/dsync/page/team-dsync-view.tsx
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/features/ee/dsync/page/team-dsync-view.tsx
🧠 Learnings (1)
packages/features/ee/dsync/page/team-dsync-view.tsx (1)

Learnt from: Anshumancanrock
PR: #22570
File: apps/web/modules/signup-view.tsx:253-253
Timestamp: 2025-07-21T21:33:23.371Z
Learning: In signup-view.tsx, when checking if redirectUrl contains certain strings, using explicit && checks (redirectUrl && redirectUrl.includes()) is preferred over optional chaining (redirectUrl?.includes()) to ensure the result is always a boolean rather than potentially undefined. This approach provides cleaner boolean contracts for downstream conditional logic.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Detect changes
🔇 Additional comments (1)
packages/features/ee/dsync/page/team-dsync-view.tsx (1)

15-15: LGTM: Clean prop interface for PBAC integration.

The optional permissions prop with canEdit boolean follows the established pattern for PBAC integration across organization settings components.

Copy link
Contributor

@emrysal emrysal left a comment

Choose a reason for hiding this comment

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

Nice improvements!

@sean-brydon sean-brydon merged commit 4b2f89d into main Jul 31, 2025
43 checks passed
@sean-brydon sean-brydon deleted the feat/org-settings-pbac branch July 31, 2025 00:56
anikdhabal added a commit that referenced this pull request Jul 31, 2025
* fix: resolve username constraint violation in removeMember

- Update username to - format when removing users from organizations
- Fix unique constraint violation on (username, organizationId) when organizationId is null
- Add test case to verify username format change and successful removal
- Fix skipped test by adding proper imports and removing describe.skip

Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>

* test: update removeMember test to verify constraint violation scenario

- Create two users with same username (one with null orgId, one with orgId)
- Verify removing org user updates username without unique constraint error
- Test ensures username gets updated to - format

Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>

* test: add comprehensive unit tests for handleInstantMeeting

- Revert previous removeMember changes
- Add full test coverage for instant meeting functionality
- Test team validation, booking creation, token generation
- Test webhook triggers and browser notifications
- Mock external dependencies for isolated unit tests
- Translation issue to be addressed in future iteration

Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>

* update

* Update handleInstantMeeting.test.ts

* fix: redir parameter for connect atoms (#22815)

* encode redirect url to make sure it has all parameters

* add changesets

* feat: Support an array response for a field when used as `Value of Field` (#22740)

* Passing tests and fixed

* self review addressed adn more tests

* fix: flaky e2e (#22819)

* fix: merge working hours when adjacent (#21912)

* fix: Adjacency issue when working hours connect over multiple days

* Add tests to validate the new merging of day end logic

* Update to correct annotation.

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

* Implement subsequent date ranges for date overrides also

* The map needs to be updated on successful resolve.

* test: add failing test for overlapping ranges with same end time

Demonstrates bug where overlapping working hour ranges (6:00-10:00 and 8:00-10:00)
lose the earlier portion (6:00-8:00), showing only 8:00 and 9:00 slots
instead of all 4 slots (6:00, 7:00, 8:00, 9:00).

Related to Carina's comment on PR #21912.

Co-Authored-By: alex@cal.com <me@alexvanandel.com>

* fix: properly merge overlapping ranges with same end time

Fixes bug where overlapping working hour ranges with the same end time
(e.g., 6:00-10:00 and 8:00-10:00) would lose the earlier portion of the
first range. The merging logic now correctly preserves the earliest
start time when ranges overlap and share the same end time.

This ensures all expected time slots are available (6:00, 7:00, 8:00, 9:00)
instead of losing the earlier slots (6:00, 7:00).

Resolves the issue identified in Carina's comment on PR #21912.

Co-Authored-By: alex@cal.com <me@alexvanandel.com>

* perf: optimize overlapping range detection from O(n²) to O(n)

Replaces Object.keys().find() with Map-based lookup for ranges with same end time.
This optimization handles 2000+ date ranges efficiently, reducing complexity from
4M operations to linear time while maintaining the same merging behavior.

Performance improvement for high-volume event types with many availability ranges.

Co-Authored-By: alex@cal.com <me@alexvanandel.com>

---------

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* feat: Improving Booking Visibility at Month-End (#22770)

* remove first weeks and add last

* fix added last week

* prefetch availability of next month

* don't switch month

* On hover show month

* only show new UI in monthly view

* show month tooldtip only when needed

* show month on first day of month

* remove isFirstDayOfNextMonth

* fix prefetching next month

* fix datePicker tests

* preventMonthSwitching in monthly view

* add tests

* code clean up

* code clean up

* code clena up for ooo days

* push first day of month

* remove bg color for the month badge

* fix text colour

* remove not needed

* use object param

* revert: use object param

* use object param

* fix DatePicker tests

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Sean Brydon <sean@cal.com>
Co-authored-by: Eunjae Lee <hey@eunjae.dev>

* chore: Refactor logs (#22824)

* Refactor logs

* Add specific info to log

* fix: Errors in org onboarding in some edge cases (#22711)

* Automatically enable migration of a team that conflicts with Orgs slug

* Allow changing the orgs slug and name if user goes back and changes it

* avoid crashing on some scenarios

* Revert "Allow changing the orgs slug and name if user goes back and changes it"

This reverts commit f8872b0.

* fix: handle unpublished teams gracefully in org migration

- Change 'No oldSlug for team' from error to warning for unpublished teams
- Keep 'No slug for team' as error since org onboarding ensures teams have names
- Add test for migrating unpublished teams without oldSlug
- Add clarifying comments about when each condition can occur

* feat: enable PBAC checking on organization settings page (#22467)

* refactor: convert checkBookingLimits to class service with dependency injection (#22768)

* refactor: convert checkBookingLimits to class service with dependency injection

- Create CheckBookingLimitsService class following AvailableSlotsService pattern
- Add countBookingsByEventTypeAndDateRange method to BookingRepository
- Move direct prisma calls from service to repository layer
- Implement dependency injection with proper DI tokens and modules
- Update all usage points to use the new service through DI
- Maintain backward compatibility with error-throwing wrapper functions
- Update tests to use the new service pattern
- Resolve TODO comment in AvailableSlotsService for DI integration

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* chore: DI CheckBookingLimitsService in v2 slots service

* chore: bump libraries

* chore: create getCheckBookingLimitsService

* refactor: convert checkBookingAndDurationLimits to service class with DI

- Create CheckBookingAndDurationLimitsService class following DI pattern
- Add DI tokens and module for the new service
- Update booking-limits container to provide the new service
- Refactor handleNewBooking.ts to use service through DI
- Maintain backward compatibility with deprecated function export
- Preserve all existing functionality while improving code organization

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* chore: CheckBookingAndDurationLimitsService

* chore: bump platform libs

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: morgan@cal.com <morgan@cal.com>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>

* fix: Return empty available days if error querying calendar (#22828)

* Return a busy block placeholder if calendar throws an error

* Refactor `getCalendarsEvents` to return an object with a success prop

* Throw error in `getBusyTimes` if failed to fetch calendar availability

* Return empty available days if error getting busy times

* yeet.

* Type fix

* Fix type error in getLuckyUsers

* Type fixes

* Type fix

* Type fix

* Fix test

* Fix test mocks

* Refactor calendars.service to use new calendarBusyTimesQuery

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>

* chore: release v5.5.9

* include mobile layout (#22836)

Co-authored-by: CarinaWolli <wollencarina@gmail.com>

* test: fix handleInstantMeeting test with setupAndTeardown and proper mocking

- Add setupAndTeardown() for proper test environment setup
- Use mockNoTranslations() to fix translation function mocking
- Simplify NextApiRequest mock to resolve TypeScript errors
- Both test cases now pass successfully

Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>

* fix typo

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Rajiv Sahal <sahalrajiv-extc@atharvacoe.ac.in>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Sean Brydon <sean@cal.com>
Co-authored-by: Eunjae Lee <hey@eunjae.dev>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: morgan@cal.com <morgan@cal.com>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
consumer core area: core, team members only enterprise area: enterprise, audit log, organisation, SAML, SSO ✨ feature New feature or request ❗️ migrations contains migration files organizations area: organizations, orgs ready-for-e2e
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants