Skip to content

Comments

fix: apply PBAC to routing form CRUD#22859

Merged
hariombalhara merged 23 commits intomainfrom
fix/pbac-routing-form
Aug 8, 2025
Merged

fix: apply PBAC to routing form CRUD#22859
hariombalhara merged 23 commits intomainfrom
fix/pbac-routing-form

Conversation

@eunjae-lee
Copy link
Contributor

@eunjae-lee eunjae-lee commented Aug 1, 2025

What does this PR do?

This PR applies PBAC to routing form CRUD.

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • N/A - I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. If N/A, write N/A here and check the checkbox.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 1, 2025

Walkthrough

This change set implements a detailed policy-based access control (PBAC) mechanism for routing forms. It enhances server-side permission checks by distinguishing personal and team-scoped forms, verifying ownership and team membership, and retrieving granular permissions for create, read, update, and delete actions. A new checkPermissionOnExistingRoutingForm function centralizes these checks. The UI components and pages are updated to accept and propagate a new permissions prop, which controls action availability in the interface. The backend repository layer is refactored to use a new Prisma-based repository class with strong typing and extended data retrieval capabilities. Additionally, a database migration adds routing form permissions to roles, and test files are updated to reflect repository changes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Complexity: The changes span multiple layers including UI components, server-side logic, permission enforcement, repository implementation, type definitions, and database migration. They involve security-critical permission checks, interface expansions, and refactoring of data access patterns.
  • Estimated review time: Around 45 minutes to thoroughly assess permission logic correctness, type safety, repository functionality, UI integration, and migration scripts.

Note

🔌 MCP (Model Context Protocol) integration is now available in Early Access!

Pro users can now connect to remote MCP servers under the Integrations page to get reviews and chat conversations that understand additional development context.


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 006bea1 and 7486c97.

📒 Files selected for processing (1)
  • 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
⏰ 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: Install dependencies / Yarn install & cache
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/pbac-routing-form

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.
  • 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.

Support

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

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.

@vercel
Copy link

vercel bot commented Aug 1, 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) Aug 8, 2025 8:52am
cal-eu ⬜️ Ignored (Inspect) Aug 8, 2025 8:52am

@graphite-app graphite-app bot requested a review from a team August 5, 2025 09:30
@eunjae-lee eunjae-lee marked this pull request as draft August 5, 2025 09:30
@dosubot dosubot bot added routing-forms area: routing forms, routing, forms 🐛 bug Something isn't working labels Aug 5, 2025
@graphite-app
Copy link

graphite-app bot commented Aug 5, 2025

Graphite Automations

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

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

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 (5)
packages/lib/server/repository/PrismaRoutingFormRepository.ts (1)

34-68: Coding guideline violation: Use select instead of include.

The findFormByIdIncludeUserTeamAndOrg method violates the coding guideline that specifies "For Prisma queries, only select data you need; never use include, always use select".

Consider refactoring this method to use select instead of include:

  static async findFormByIdIncludeUserTeamAndOrg(formId: string) {
    return await prisma.app_RoutingForms_Form.findUnique({
      where: {
        id: formId,
      },
-     include: {
+     select: {
+       id: true,
+       description: true,
+       position: true,
+       routes: true,
+       createdAt: true,
+       updatedAt: true,
+       name: true,
+       fields: true,
+       updatedById: true,
+       userId: true,  
+       teamId: true,
+       disabled: true,
+       settings: true,
        user: {
          select: {
            id: true,
            username: true,
            email: true,
            movedToProfileId: true,
            metadata: true,
            organization: {
              select: {
                slug: true,
              },
            },
          },
        },
        team: {
          select: {
            parentId: true,
            parent: {
              select: {
                slug: true,
              },
            },
            slug: true,
            metadata: true,
          },
        },
      },
    });
  }
packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx (2)

112-112: Use t() for text localization.

Hard-coded text should be localized using the t() function per coding guidelines for .tsx files.

-                  Write to Salesforce contact/lead record
+                  {t("write_to_salesforce_contact_lead_record")}

270-270: Use t() for text localization.

Hard-coded text in toast messages should be localized using the t() function per coding guidelines.

-                      showToast("Field already exists", "error");
+                      showToast(t("field_already_exists"), "error");
packages/app-store/routing-forms/trpc/formQuery.handler.ts (1)

23-40: Use select instead of include in Prisma query

Per coding guidelines, Prisma queries should only select the data needed and never use include.

-    include: {
-      team: { select: { slug: true, name: true } },
-      _count: {
-        select: {
-          responses: true,
-        },
-      },
-    },
+    select: {
+      id: true,
+      userId: true,
+      teamId: true,
+      name: true,
+      description: true,
+      routes: true,
+      fields: true,
+      settings: true,
+      disabled: true,
+      position: true,
+      createdAt: true,
+      updatedAt: true,
+      duplicateFrom: true,
+      migrateJSRouterConfigOnUpgrade: true,
+      team: {
+        select: {
+          slug: true,
+          name: true,
+        },
+      },
+      _count: {
+        select: {
+          responses: true,
+        },
+      },
+    },
packages/app-store/routing-forms/trpc/formMutation.handler.ts (1)

15-15: Remove unused import

The isFormCreateEditAllowed import is no longer used after refactoring to the new permission system.

-import { isFormCreateEditAllowed } from "../lib/isFormCreateEditAllowed";
🧹 Nitpick comments (1)
packages/app-store/routing-forms/trpc/permissions.ts (1)

39-55: LGTM!

The team-scoped permission check correctly integrates with the PBAC system and provides appropriate error handling with descriptive messages.

Consider optimizing by injecting the PermissionCheckService as a dependency rather than creating new instances, especially if this function is called frequently.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between cd576f5 and 7956c17.

📒 Files selected for processing (16)
  • packages/app-store/routing-forms/components/SingleForm.tsx (2 hunks)
  • packages/app-store/routing-forms/components/_components/Header.tsx (6 hunks)
  • packages/app-store/routing-forms/components/getServerSidePropsSingleForm.ts (3 hunks)
  • packages/app-store/routing-forms/pages/form-edit/[...appPages].tsx (2 hunks)
  • packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx (1 hunks)
  • packages/app-store/routing-forms/pages/reporting/[...appPages].tsx (2 hunks)
  • packages/app-store/routing-forms/pages/route-builder/[...appPages].tsx (1 hunks)
  • packages/app-store/routing-forms/trpc/deleteForm.handler.ts (2 hunks)
  • packages/app-store/routing-forms/trpc/formMutation.handler.ts (3 hunks)
  • packages/app-store/routing-forms/trpc/formQuery.handler.ts (2 hunks)
  • packages/app-store/routing-forms/trpc/permissions.ts (1 hunks)
  • packages/app-store/routing-forms/types/shared.ts (1 hunks)
  • packages/features/pbac/domain/types/permission-registry.ts (2 hunks)
  • packages/lib/server/getRoutedUrl.test.ts (1 hunks)
  • packages/lib/server/getRoutedUrl.ts (2 hunks)
  • packages/lib/server/repository/PrismaRoutingFormRepository.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.ts

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

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • packages/lib/server/getRoutedUrl.ts
  • packages/lib/server/repository/PrismaRoutingFormRepository.ts
  • packages/app-store/routing-forms/trpc/deleteForm.handler.ts
  • packages/app-store/routing-forms/types/shared.ts
  • packages/lib/server/getRoutedUrl.test.ts
  • packages/app-store/routing-forms/trpc/formQuery.handler.ts
  • packages/app-store/routing-forms/trpc/formMutation.handler.ts
  • packages/app-store/routing-forms/components/getServerSidePropsSingleForm.ts
  • packages/features/pbac/domain/types/permission-registry.ts
  • packages/app-store/routing-forms/trpc/permissions.ts
**/*.{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/lib/server/getRoutedUrl.ts
  • packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx
  • packages/app-store/routing-forms/components/SingleForm.tsx
  • packages/app-store/routing-forms/pages/reporting/[...appPages].tsx
  • packages/lib/server/repository/PrismaRoutingFormRepository.ts
  • packages/app-store/routing-forms/trpc/deleteForm.handler.ts
  • packages/app-store/routing-forms/types/shared.ts
  • packages/app-store/routing-forms/pages/form-edit/[...appPages].tsx
  • packages/lib/server/getRoutedUrl.test.ts
  • packages/app-store/routing-forms/trpc/formQuery.handler.ts
  • packages/app-store/routing-forms/pages/route-builder/[...appPages].tsx
  • packages/app-store/routing-forms/trpc/formMutation.handler.ts
  • packages/app-store/routing-forms/components/getServerSidePropsSingleForm.ts
  • packages/features/pbac/domain/types/permission-registry.ts
  • packages/app-store/routing-forms/trpc/permissions.ts
  • packages/app-store/routing-forms/components/_components/Header.tsx
**/*.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/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx
  • packages/app-store/routing-forms/components/SingleForm.tsx
  • packages/app-store/routing-forms/pages/reporting/[...appPages].tsx
  • packages/app-store/routing-forms/pages/form-edit/[...appPages].tsx
  • packages/app-store/routing-forms/pages/route-builder/[...appPages].tsx
  • packages/app-store/routing-forms/components/_components/Header.tsx
**/*Repository.ts

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

Repository files must include Repository suffix, prefix with technology if applicable (e.g., PrismaAppRepository.ts), and use PascalCase matching the exported class

Files:

  • packages/lib/server/repository/PrismaRoutingFormRepository.ts
🧬 Code Graph Analysis (5)
packages/lib/server/getRoutedUrl.ts (1)
packages/lib/server/repository/PrismaRoutingFormRepository.ts (1)
  • PrismaRoutingFormRepository (20-69)
packages/app-store/routing-forms/components/SingleForm.tsx (1)
packages/app-store/routing-forms/types/shared.ts (1)
  • SingleFormComponentProps (8-20)
packages/app-store/routing-forms/trpc/deleteForm.handler.ts (2)
packages/app-store/routing-forms/trpc/permissions.ts (1)
  • checkPermissionOnExistingRoutingForm (8-56)
packages/platform/libraries/index.ts (1)
  • MembershipRole (98-98)
packages/app-store/routing-forms/types/shared.ts (1)
packages/app-store/routing-forms/components/getServerSidePropsSingleForm.ts (1)
  • getServerSidePropsForSingleFormView (10-167)
packages/app-store/routing-forms/trpc/permissions.ts (3)
packages/platform/libraries/index.ts (2)
  • MembershipRole (98-98)
  • TRPCError (56-56)
packages/lib/server/repository/PrismaRoutingFormRepository.ts (1)
  • PrismaRoutingFormRepository (20-69)
packages/features/pbac/services/permission-check.service.ts (2)
  • PermissionCheckService (20-370)
  • hasPermission (205-234)
🔇 Additional comments (27)
packages/lib/server/repository/PrismaRoutingFormRepository.ts (1)

20-32: LGTM! Well-implemented repository method with proper typing.

The new findById method follows best practices with:

  • Proper generic constraints for type safety
  • Flexible field selection via options parameter
  • Adherence to the coding guideline of using select instead of include
packages/lib/server/getRoutedUrl.ts (2)

24-24: LGTM! Clean migration to the new repository class.

The import change aligns with the introduction of PrismaRoutingFormRepository and maintains the same functionality.


97-97: LGTM! Consistent usage of the new repository method.

The method call correctly uses the new PrismaRoutingFormRepository.findFormByIdIncludeUserTeamAndOrg method.

packages/lib/server/getRoutedUrl.test.ts (1)

17-17: LGTM! Smart aliasing maintains test compatibility.

The import change with aliasing (as RoutingFormRepository) is a clean way to migrate to the new repository class while avoiding the need to update all test references.

packages/app-store/routing-forms/types/shared.ts (1)

19-19: LGTM! Clean type extension for PBAC permissions.

The addition of the permissions property using inferSSRProps ensures type safety and consistency with the server-side props implementation. This properly supports the new Policy-Based Access Control functionality.

packages/app-store/routing-forms/pages/route-builder/[...appPages].tsx (2)

1432-1432: LGTM! Clean prop addition for PBAC support.

The addition of the permissions prop to the RouteBuilder component props correctly supports the Policy-Based Access Control implementation.


1440-1440: LGTM! Proper prop forwarding to SingleForm.

The permissions prop is correctly passed down to the SingleForm component, maintaining the data flow for access control enforcement.

packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx (2)

323-323: LGTM: Permissions prop correctly added to component signature.

This change aligns with the PBAC implementation being applied across routing form components.


330-330: LGTM: Permissions prop correctly passed to SingleForm.

This completes the prop threading pattern for PBAC implementation.

packages/app-store/routing-forms/pages/reporting/[...appPages].tsx (1)

269-269: LGTM: Permissions prop correctly implemented.

The permissions prop is properly added to the component signature and passed to SingleForm, following the established PBAC pattern.

Also applies to: 284-284

packages/app-store/routing-forms/components/SingleForm.tsx (2)

75-81: LGTM: Function signature correctly updated with permissions prop.

The permissions prop is properly included in the SingleFormComponentProps destructuring, aligning with the type definition.


162-162: LGTM: Permissions correctly passed to Header component.

This continues the proper prop threading pattern for PBAC implementation to control UI elements.

packages/app-store/routing-forms/pages/form-edit/[...appPages].tsx (1)

351-351: LGTM: Permissions prop correctly implemented.

The permissions prop is properly added and passed to SingleForm, maintaining consistency with the PBAC implementation pattern.

Also applies to: 360-360

packages/app-store/routing-forms/trpc/deleteForm.handler.ts (2)

3-3: LGTM: Required imports added for new permission system.

The MembershipRole and checkPermissionOnExistingRoutingForm imports are necessary for the enhanced PBAC implementation.

Also applies to: 10-10


22-27: LGTM: Proper implementation of granular permission checking.

The new permission check uses the centralized checkPermissionOnExistingRoutingForm function with appropriate:

  • Permission string: "routingForm.delete" for delete operations
  • Fallback roles: ADMIN and OWNER for team-scoped forms
  • Proper error handling through the centralized function

This replaces the previous generic permission check with a more granular PBAC approach.

packages/app-store/routing-forms/trpc/formQuery.handler.ts (1)

46-52: Permission check implementation looks good

The permission check is correctly placed after verifying form existence and uses appropriate fallback roles for read access.

packages/app-store/routing-forms/components/_components/Header.tsx (2)

62-76: Permission-based UI controls implemented correctly

The Actions component properly uses the permissions object to disable buttons based on user permissions. The implementation is clean and follows best practices.

Also applies to: 175-175, 193-193


218-231: Permissions correctly propagated through component hierarchy

The Header component properly receives and passes the permissions prop to the Actions component in both mobile and desktop layouts.

Also applies to: 343-343, 377-377

packages/features/pbac/domain/types/permission-registry.ts (1)

10-10: Well-structured permission registry for RoutingForm resource

The new RoutingForm resource and its permissions follow the established pattern. The Manage action is appropriately scoped to Organization level, and all i18n keys and descriptions are consistent with other resources.

Also applies to: 388-423

packages/app-store/routing-forms/components/getServerSidePropsSingleForm.ts (2)

92-97: Good security practice for personal form access control

Returning 404 instead of 403 prevents information leakage about form existence. The ownership check is straightforward and secure.


119-152: Well-implemented permission checking for team-scoped forms

The permission logic correctly:

  • Defaults to full permissions for personal forms
  • Validates team membership before checking permissions
  • Uses appropriate fallback roles (MEMBER for read, ADMIN/OWNER for write operations)
  • Returns 404 for unauthorized access to prevent information leakage
packages/app-store/routing-forms/trpc/formMutation.handler.ts (1)

37-62: Well-structured permission checks for form mutations

The implementation correctly handles three scenarios:

  1. Updates - uses checkPermissionOnExistingRoutingForm with update permission
  2. Team form creation - uses PermissionCheckService with create permission
  3. Personal form creation - implicitly allowed (no teamId)

The error handling provides a clear, descriptive message for forbidden access.

packages/app-store/routing-forms/trpc/permissions.ts (5)

1-6: LGTM!

The imports are well-organized and include all necessary dependencies for implementing PBAC permission checks on routing forms.


8-18: LGTM!

The function signature is well-designed with descriptive naming and proper TypeScript typing. The parameter structure clearly defines the required inputs for permission checking.


20-22: LGTM!

Excellent adherence to coding guidelines by using select instead of include and only fetching the minimal required fields for permission checking.


24-29: LGTM!

Proper error handling with appropriate TRPC error code and clear messaging for the not found case.


31-37: LGTM!

The personal-scoped form permission check correctly implements ownership-based access control with appropriate error handling.

@eunjae-lee eunjae-lee marked this pull request as ready for review August 5, 2025 14:24
@github-actions
Copy link
Contributor

github-actions bot commented Aug 7, 2025

E2E results are ready!

@eunjae-lee eunjae-lee requested a review from a team as a code owner August 7, 2025 12:32
@eunjae-lee
Copy link
Contributor Author

@hariombalhara regarding your feedback, I just did it here. It's not disabling the list item, but puts "read only" badge only now.

hariombalhara
hariombalhara previously approved these changes Aug 8, 2025
- Fix TypeScript error in routing forms component
- Change readonly={readOnly} to readOnly={readOnly} to match component interface
- Resolves type check failure in CI

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
hariombalhara
hariombalhara previously approved these changes Aug 8, 2025
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.

Looks good 👍

@hariombalhara hariombalhara merged commit 6fe6540 into main Aug 8, 2025
38 checks passed
@hariombalhara hariombalhara deleted the fix/pbac-routing-form branch August 8, 2025 12:18
pallava-joshi pushed a commit to pallava-joshi/cal.com that referenced this pull request Aug 8, 2025
* fix: apply PBAC to routing form CRUD

* apply permission checks to the UI

* moving prisma call to repository [WIP]

* rename repository and fix type error

* update implementation

* fix formMutation handler

* remove unused import

* revert some rename

* add RolePermission for 'routingForm'

* Revert "revert some rename"

This reverts commit 0ef3114.

* clean up PrismaRoutingFormRepository

* fix unit test

* remove no longer necessary code

* fix type definition

* explicit permission handling

* do not disable un-editable routing form

* fix: correct property name from readonly to readOnly in ListLinkItem

- Fix TypeScript error in routing forms component
- Change readonly={readOnly} to readOnly={readOnly} to match component interface
- Resolves type check failure in CI

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

---------

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@coderabbitai coderabbitai bot mentioned this pull request Sep 11, 2025
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐛 bug Something isn't working consumer core area: core, team members only ❗️ migrations contains migration files ready-for-e2e routing-forms area: routing forms, routing, forms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants