Skip to content

Fix/org owners edit own username#24378

Closed
antcybersec wants to merge 3 commits intocalcom:mainfrom
antcybersec:fix/org-owners-edit-own-username
Closed

Fix/org owners edit own username#24378
antcybersec wants to merge 3 commits intocalcom:mainfrom
antcybersec:fix/org-owners-edit-own-username

Conversation

@antcybersec
Copy link

Fix: Allow org owners and admins to edit their own usernames
Problem: Org owners and admins couldn't see the triple dot menu to edit their own usernames in the organization member list.
Solution: Modified permission logic to allow org owners/admins to edit themselves while maintaining security for regular members.
Changes:
UserListTable.tsx: Allow edit for self if user is admin/owner
MemberList.tsx: Allow edit for self if user is org admin/owner
Security: Only users with proper admin/owner permissions can edit themselves; regular members still cannot.
Fixes #24377

…rnames

- Fix UserListTable to show edit option for org owners/admins on themselves
- Fix MemberList to show edit option for org owners/admins on themselves
- Resolves issue where org owners/admins couldn't see triple dot menu to edit their own usernames
- Maintains existing security by only allowing edit for users with proper permissions

Fixes calcom#24377
@antcybersec antcybersec requested a review from a team as a code owner October 9, 2025 05:53
@graphite-app graphite-app bot added the community Created by Linear-GitHub Sync label Oct 9, 2025
@graphite-app graphite-app bot requested a review from a team October 9, 2025 05:54
@github-actions
Copy link
Contributor

github-actions bot commented Oct 9, 2025

Hey there and thank you for opening this pull request! 👋🏼

We require pull request titles to follow the Conventional Commits specification and it looks like your proposed title needs to be adjusted.

Details:

No release type found in pull request title "Fix/org owners edit own username". Add a prefix to indicate what kind of release this pull request corresponds to. For reference, see https://www.conventionalcommits.org/

Available types:
 - feat: A new feature
 - fix: A bug fix
 - docs: Documentation only changes
 - style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
 - refactor: A code change that neither fixes a bug nor adds a feature
 - perf: A code change that improves performance
 - test: Adding missing tests or correcting existing tests
 - build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
 - ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
 - chore: Other changes that don't modify src or test files
 - revert: Reverts a previous commit

@vercel
Copy link

vercel bot commented Oct 9, 2025

@antcybersec is attempting to deploy a commit to the cal Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions bot added organizations area: organizations, orgs ✨ feature New feature or request labels Oct 9, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 9, 2025

Walkthrough

This change updates Google Calendar integration to support per-event custom reminder minutes during create and update, modifies guestsCanSeeOtherGuests based on seatsPerTimeSlot, and adjusts an error type guard. It adds optional customReminderMinutes to CalendarEvent and CalendarServiceEvent types. In the teams and users UI, edit permissions now allow org admins/owners to edit their own entries; minor refactors include local variable renames and added braces in switch cases. MemberList’s Props gains facetedTeamValues and permissions fields; internal hook variable is renamed. No exported function signatures are changed beyond the added optional type fields.

Possibly related PRs

  • feat: assign custom roles #22662: Also modifies packages/features/ee/teams/components/MemberList.tsx to introduce/adjust facetedTeamValues and role-related handling, indicating closely related code paths.

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning This PR includes unrelated modifications to the GoogleCalendar service and types (custom reminder logic), along with Prop interface additions and local variable renames that fall outside the scope of fixing username edit permissions. Please remove or isolate the calendar reminder updates and other structural refactors into separate PRs so that this change only addresses the username edit permission fix.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly highlights the primary change of allowing organization owners and admins to edit their own usernames, matching the main intent of the PR without extraneous detail.
Linked Issues Check ✅ Passed The changes in MemberList.tsx and UserListTable.tsx directly implement the objective of issue #24377 by adjusting the edit permission logic to allow org owners and admins to edit their own usernames, satisfying the linked issue requirements.
Description Check ✅ Passed The description clearly outlines the issue with org owners and admins not seeing the edit menu and details the specific permission logic changes in UserListTable.tsx and MemberList.tsx that address this problem.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@dosubot dosubot bot added the 🐛 bug Something isn't working label Oct 9, 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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/features/ee/teams/components/MemberList.tsx (1)

66-70: Remove the duplicate Props interface definition.

This Props interface is shadowed by the second definition at lines 150-166, making it dead code. The second definition includes additional required fields (facetedTeamValues and permissions) that are actually used in the component.

Apply this diff to remove the duplicate:

-interface Props {
-  team: NonNullable<RouterOutputs["viewer"]["teams"]["get"]>;
-  isOrgAdminOrOwner: boolean | undefined;
-  setShowMemberInvitationModal: Dispatch<SetStateAction<boolean>>;
-}
-
🧹 Nitpick comments (3)
packages/types/Calendar.d.ts (1)

267-268: Note: Redundant field declaration.

Since CalendarServiceEvent extends CalendarEvent (line 266), the customReminderMinutes field is inherited. While explicit redeclaration can aid clarity, it introduces maintenance risk if the types diverge.

Consider removing this redundant declaration to avoid duplication:

 export interface CalendarServiceEvent extends CalendarEvent {
-  // Optional per-calendar reminder override in minutes for integrations that support it
-  customReminderMinutes?: number;
   calendarDescription: string;
 }
packages/app-store/googlecalendar/lib/CalendarService.ts (1)

219-219: Confirm guest visibility logic
Change aligns with the Office365 implementation (when seatsPerTimeSlot is set, guestsCanSeeOtherGuests ⇄ seatsShowAttendees). Consider adding a test case for a truthy seatsPerTimeSlot with seatsShowAttendees=false to assert guestsCanSeeOtherGuests=false.

packages/features/ee/teams/components/MemberList.tsx (1)

690-695: Consider clarifying the intentionally unused return value.

The underscore prefix appropriately indicates this value is intentionally unused. However, if the hook's return value is truly unnecessary, consider using void or adding a comment to make the intent explicit.

Alternative approaches:

-  const _fetchMoreOnBottomReached = useFetchMoreOnBottomReached({
+  // Hook sets up scroll listener for infinite pagination; return value unused
+  useFetchMoreOnBottomReached({
     tableContainerRef,
     hasNextPage,
     fetchNextPage,
     isFetching,
   });

or

-  const _fetchMoreOnBottomReached = useFetchMoreOnBottomReached({
+  void useFetchMoreOnBottomReached({
     tableContainerRef,
     hasNextPage,
     fetchNextPage,
     isFetching,
   });
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between bf89991 and 09ff5e8.

📒 Files selected for processing (4)
  • packages/app-store/googlecalendar/lib/CalendarService.ts (5 hunks)
  • packages/features/ee/teams/components/MemberList.tsx (3 hunks)
  • packages/features/users/components/UserTable/UserListTable.tsx (4 hunks)
  • packages/types/Calendar.d.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.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/types/Calendar.d.ts
  • packages/app-store/googlecalendar/lib/CalendarService.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/types/Calendar.d.ts
  • packages/app-store/googlecalendar/lib/CalendarService.ts
  • packages/features/users/components/UserTable/UserListTable.tsx
  • packages/features/ee/teams/components/MemberList.tsx
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/types/Calendar.d.ts
  • packages/app-store/googlecalendar/lib/CalendarService.ts
  • packages/features/users/components/UserTable/UserListTable.tsx
  • packages/features/ee/teams/components/MemberList.tsx
**/*Service.ts

📄 CodeRabbit inference engine (.cursor/rules/review.mdc)

Service files must include Service suffix, use PascalCase matching exported class, and avoid generic names (e.g., MembershipService.ts)

Files:

  • packages/app-store/googlecalendar/lib/CalendarService.ts
**/*.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/users/components/UserTable/UserListTable.tsx
  • packages/features/ee/teams/components/MemberList.tsx
⏰ Context from checks skipped due to timeout of 180000ms. 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: Codacy Static Code Analysis
🔇 Additional comments (7)
packages/features/users/components/UserTable/UserListTable.tsx (1)

451-461: Self-edit now correctly unlocked for admins/owners.

The updated predicate lets accepted admins/owners reach their own edit actions while keeping regular members restricted. Matches the PR goal without loosening other checks.

packages/types/Calendar.d.ts (1)

214-215: LGTM: Well-documented optional field addition.

The optional customReminderMinutes field is clearly documented and appropriately typed. The comment explains its purpose for integrations that support custom reminders.

packages/app-store/googlecalendar/lib/CalendarService.ts (4)

45-46: LGTM: Safer property check.

Using Object.prototype.hasOwnProperty.call instead of direct hasOwnProperty is a best practice that avoids potential issues with objects that don't inherit from Object.prototype or have overridden the method.


205-218: LGTM: Correct reminder override logic.

The conditional logic correctly applies custom reminders when customReminderMinutes is defined and finite, with appropriate safeguards:

  • Number.isFinite() check prevents NaN and Infinity
  • Math.max(0, ...) clamps negative values to zero
  • Math.floor() ensures integer minutes

383-396: LGTM: Consistent reminder override logic.

The reminder logic in updateEvent correctly mirrors the implementation in createEvent, maintaining consistency across event creation and updates.


397-397: Verify consistency: guestsCanSeeOtherGuests behavioral change.

This change mirrors the one in createEvent (line 219), ensuring consistent behavior between event creation and updates. The conditional logic based on seatsPerTimeSlot is correct.

Confirm this behavioral change is documented and tested, as it affects how guests see other attendees in seated events.

packages/features/ee/teams/components/MemberList.tsx (1)

436-437: Verify isOrgAdminOrOwner isn’t undefined
props.isOrgAdminOrOwner is typed as boolean | undefined; if it’s undefined when isSelf is true, editMode will be false and org-admins/owners couldn’t edit themselves. Ensure this prop is always a boolean at runtime or coalesce it (e.g. !!props.isOrgAdminOrOwner).

Comment on lines +184 to +191
// Determine optional custom reminder minutes from event payload or destination calendar metadata
// We intentionally access via `any` to avoid widening Calendar types and to keep this change isolated.
const customReminderMinutes: number | undefined =
(calEvent as any)?.customReminderMinutes ??
(calEvent.destinationCalendar?.find((cal) => cal.credentialId === credentialId)
? (calEvent.destinationCalendar.find((cal) => cal.credentialId === credentialId) as any)
?.customReminderMinutes
: undefined);
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 | 🟠 Major

Remove unnecessary any-casts for type safety.

The type definitions in packages/types/Calendar.d.ts now include customReminderMinutes?: number on both CalendarEvent and CalendarServiceEvent. Since calEvent is typed as CalendarServiceEvent, you can access this field directly without casting to any.

The comment claims this avoids "widening Calendar types," but the types have already been widened in the type definition file. These casts bypass type safety without benefit.

Apply this diff to access the field safely:

-    // Determine optional custom reminder minutes from event payload or destination calendar metadata
-    // We intentionally access via `any` to avoid widening Calendar types and to keep this change isolated.
-    const customReminderMinutes: number | undefined =
-      (calEvent as any)?.customReminderMinutes ??
-      (calEvent.destinationCalendar?.find((cal) => cal.credentialId === credentialId)
-        ? (calEvent.destinationCalendar.find((cal) => cal.credentialId === credentialId) as any)
-            ?.customReminderMinutes
-        : undefined);
+    // Determine optional custom reminder minutes from event payload or destination calendar metadata
+    const customReminderMinutes: number | undefined =
+      calEvent.customReminderMinutes ??
+      calEvent.destinationCalendar?.find((cal) => cal.credentialId === credentialId)?.customReminderMinutes;
📝 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
// Determine optional custom reminder minutes from event payload or destination calendar metadata
// We intentionally access via `any` to avoid widening Calendar types and to keep this change isolated.
const customReminderMinutes: number | undefined =
(calEvent as any)?.customReminderMinutes ??
(calEvent.destinationCalendar?.find((cal) => cal.credentialId === credentialId)
? (calEvent.destinationCalendar.find((cal) => cal.credentialId === credentialId) as any)
?.customReminderMinutes
: undefined);
// Determine optional custom reminder minutes from event payload or destination calendar metadata
const customReminderMinutes: number | undefined =
calEvent.customReminderMinutes ??
calEvent.destinationCalendar?.find((cal) => cal.credentialId === credentialId)?.customReminderMinutes;
🤖 Prompt for AI Agents
In packages/app-store/googlecalendar/lib/CalendarService.ts around lines 184 to
191, remove the unnecessary any casts and access customReminderMinutes directly
from the typed CalendarServiceEvent and its destinationCalendar entries; replace
the double any-cast/find with a single typed find result (e.g., assign
destinationCalendar.find(...) to a const typed as Calendar | undefined) and use
optional chaining (event.customReminderMinutes ?? found?.customReminderMinutes)
so the code relies on the declared types in packages/types/Calendar.d.ts and
preserves type safety.

Comment on lines +363 to +369
// Determine optional custom reminder minutes from event payload or destination calendar metadata
const updateCustomReminderMinutes: number | undefined =
(event as any)?.customReminderMinutes ??
(event.destinationCalendar && externalCalendarId
? ((event.destinationCalendar.find((cal) => cal.externalId === externalCalendarId) as any)
?.customReminderMinutes as number | undefined)
: undefined);
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 | 🟠 Major

Remove unnecessary any-casts for type safety (updateEvent).

Same issue as in createEvent: these any-casts are unnecessary since the types now include customReminderMinutes. Additionally, this code has a redundant find() operation - it searches destinationCalendar array twice.

Apply this diff:

-    // Determine optional custom reminder minutes from event payload or destination calendar metadata
-    const updateCustomReminderMinutes: number | undefined =
-      (event as any)?.customReminderMinutes ??
-      (event.destinationCalendar && externalCalendarId
-        ? ((event.destinationCalendar.find((cal) => cal.externalId === externalCalendarId) as any)
-            ?.customReminderMinutes as number | undefined)
-        : undefined);
+    // Determine optional custom reminder minutes from event payload or destination calendar metadata
+    const updateCustomReminderMinutes: number | undefined =
+      event.customReminderMinutes ??
+      event.destinationCalendar?.find((cal) => cal.externalId === externalCalendarId)?.customReminderMinutes;
📝 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
// Determine optional custom reminder minutes from event payload or destination calendar metadata
const updateCustomReminderMinutes: number | undefined =
(event as any)?.customReminderMinutes ??
(event.destinationCalendar && externalCalendarId
? ((event.destinationCalendar.find((cal) => cal.externalId === externalCalendarId) as any)
?.customReminderMinutes as number | undefined)
: undefined);
// Determine optional custom reminder minutes from event payload or destination calendar metadata
const updateCustomReminderMinutes: number | undefined =
event.customReminderMinutes ??
event.destinationCalendar?.find((cal) => cal.externalId === externalCalendarId)?.customReminderMinutes;
🤖 Prompt for AI Agents
In packages/app-store/googlecalendar/lib/CalendarService.ts around lines 363 to
369, the code uses unnecessary any-casts and repeats a
destinationCalendar.find() call when resolving customReminderMinutes for
updateEvent; remove the any casts and perform a single find into a typed
variable (e.g. const destCal = event.destinationCalendar?.find(c => c.externalId
=== externalCalendarId)) then set updateCustomReminderMinutes =
event.customReminderMinutes ?? destCal?.customReminderMinutes, ensuring proper
typing (number | undefined) without using any.

@antcybersec antcybersec closed this Oct 9, 2025
@antcybersec antcybersec deleted the fix/org-owners-edit-own-username branch October 9, 2025 06:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐛 bug Something isn't working community Created by Linear-GitHub Sync ✨ feature New feature or request organizations area: organizations, orgs size/M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow org owners and admins to edit their own usernames in organizations

1 participant