From 2a7c6590a3d2b20f28faa4be8f8c02c58d412995 Mon Sep 17 00:00:00 2001 From: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Date: Wed, 26 Nov 2025 13:58:29 +0000 Subject: [PATCH] feat: add permission for editUsers + implement UI (#25402) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What does this PR do? This PR implements attributes PBAC - router checks + UI ## Visual Demo (For contributors especially) A visual demonstration is strongly recommended, for both the original and new change **(video / image - any one)**. #### Video Demo (if applicable): - Show screen recordings of the issue or feature. - Demonstrate how to reproduce the issue, the behavior before and after the change. #### Image Demo (if applicable): - Add side-by-side screenshots of the original and updated change. - Highlight any significant change(s). ## Mandatory Tasks (DO NOT REMOVE) - [ ] I have self-reviewed the code (A decent size PR without self-review might be rejected). - [ ] I have updated the developer docs in /docs if this PR makes changes that would require a [documentation change](https://cal.com/docs). 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. ## How should this be tested? Enable PBAC on an org Create a custom role -> advanced -> organizations -> "editUser" Assign it to a user impersonate user test they have access to all things attributes remove permissions check they dont have permissions. ## Checklist - I haven't read the [contributing guide](https://github.com/calcom/cal.com/blob/main/CONTRIBUTING.md) - My code doesn't follow the style guidelines of this project - I haven't commented my code, particularly in hard-to-understand areas - I haven't checked if my changes generate no new warnings --- ## Summary by cubic Adds a new PBAC “editUsers” permission and read gating for Attributes, updating the UI and backend so users can view and edit attributes only when allowed. - **New Features** - Added CustomAction.EditUsers to the permission registry for organization-scoped attribute editing. - Settings computes canViewAttributes and shows the Attributes tab only when allowed. - Members page fetches Attributes permissions and exposes canViewAttributes and canEditAttributesForUser to the UI. - User Edit Sheet hides attributes without read permission and shows attribute editing and the bulk “Mass Assign Attributes” action only with editUsers; other user edits depend on changeMemberRole. - Attributes TRPC router gates create/edit/delete/toggle via PBAC and requires organization.attributes.editUsers for assign/bulk-assign; added a helper to create PBAC-aware procedures. - **Migration** - Run the new Prisma migration to seed the admin role with editUsers. - If using custom roles, grant Edit Users under Attributes as needed. Written for commit 856aa2e8e1521fc22cd71cb0fa6d720036efe8a2. Summary will update automatically on new commits. --- .../SettingsLayoutAppDirClient.tsx | 25 ++--- .../settings/(settings-layout)/layout.tsx | 9 +- .../(org-user-only)/members/page.tsx | 97 +++++++++++-------- apps/web/public/static/locales/en/common.json | 2 + .../pbac/domain/types/permission-registry.ts | 18 ++++ .../UserTable/EditSheet/EditUserForm.tsx | 10 +- .../UserTable/EditSheet/EditUserSheet.tsx | 22 +++-- .../EditSheet/SheetFooterControls.tsx | 54 ++++++++--- .../components/UserTable/UserListTable.tsx | 23 +++-- .../users/components/UserTable/types.ts | 2 + .../migration.sql | 10 ++ .../routers/viewer/attributes/_router.ts | 47 +++++---- .../server/routers/viewer/attributes/util.ts | 46 +++++++++ 13 files changed, 266 insertions(+), 99 deletions(-) create mode 100644 packages/prisma/migrations/20251126085109_pbac_add_attributes_edit_users_permission/migration.sql create mode 100644 packages/trpc/server/routers/viewer/attributes/util.ts diff --git a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/SettingsLayoutAppDirClient.tsx b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/SettingsLayoutAppDirClient.tsx index 674fa89970..0ca9440c30 100644 --- a/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/SettingsLayoutAppDirClient.tsx +++ b/apps/web/app/(use-page-wrapper)/settings/(settings-layout)/SettingsLayoutAppDirClient.tsx @@ -181,6 +181,7 @@ export interface SettingsPermissions { canViewRoles?: boolean; canViewOrganizationBilling?: boolean; canUpdateOrganization?: boolean; + canViewAttributes?: boolean; } const useTabs = ({ @@ -211,7 +212,7 @@ const useTabs = ({ (child) => permissions?.canUpdateOrganization || !organizationAdminKeys.includes(child.name) ); - if (permissions?.canUpdateOrganization) { + if (permissions?.canViewAttributes) { newArray.splice(4, 0, { name: "attributes", href: "/settings/organizations/attributes", @@ -452,7 +453,7 @@ const TeamListCollapsible = ({ teamFeatures }: { teamFeatures?: Record )} @@ -460,7 +461,7 @@ const TeamListCollapsible = ({ teamFeatures }: { teamFeatures?: Record {/* Show roles only for sub-teams with PBAC-enabled parent */} @@ -481,7 +482,7 @@ const TeamListCollapsible = ({ teamFeatures }: { teamFeatures?: Record {/* Hide if there is a parent ID */} @@ -491,7 +492,7 @@ const TeamListCollapsible = ({ teamFeatures }: { teamFeatures?: Record @@ -500,7 +501,7 @@ const TeamListCollapsible = ({ teamFeatures }: { teamFeatures?: Record @@ -578,7 +579,7 @@ const SettingsSidebarContainer = ({