From c55af91c6021a895cd1e30b43df57c740d31b0c3 Mon Sep 17 00:00:00 2001 From: Kyle Pope Date: Fri, 6 Mar 2026 17:16:35 +0800 Subject: [PATCH] Fix two shared calendar bugs: lock banner missing and calendar not found on save MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug 1 (lock banner): Owners bypassed lock acquisition entirely, so no DB lock was created when an owner edited a shared event. Members polling GET /shared-calendars/events/{id}/lock correctly saw `locked: false`. Fix: remove the `myPermission !== 'owner'` guard in handleEditStart so owners also acquire a temporary 5-min edit lock when editing shared events, making the banner visible to all other members. Bug 2 (calendar not found on save): PUT /events/{id} called _verify_calendar_ownership whenever calendar_id appeared in the payload, even when it was unchanged. For shared-calendar members this always 404'd because they don't own the calendar. Fix: add `update_data["calendar_id"] != event.calendar_id` to the guard — ownership is only verified when the calendar is actually being changed (existing M-01 guard handles the move-off-shared case). Co-Authored-By: Claude Opus 4.6 --- backend/app/routers/events.py | 4 +++- frontend/src/components/calendar/EventDetailPanel.tsx | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/backend/app/routers/events.py b/backend/app/routers/events.py index 140d046..e7bd57f 100644 --- a/backend/app/routers/events.py +++ b/backend/app/routers/events.py @@ -353,7 +353,9 @@ async def update_event( update_data["recurrence_rule"] = json.dumps({k: v for k, v in rule_obj.items() if v is not None}) if rule_obj else None # SEC-04: if calendar_id is being changed, verify the target belongs to the user - if "calendar_id" in update_data and update_data["calendar_id"] is not None: + # Only verify ownership when the calendar is actually changing — members submitting + # an unchanged calendar_id must not be rejected just because they aren't the owner. + if "calendar_id" in update_data and update_data["calendar_id"] is not None and update_data["calendar_id"] != event.calendar_id: await _verify_calendar_ownership(db, update_data["calendar_id"], current_user.id) # M-01: Block non-owners from moving events off shared calendars diff --git a/frontend/src/components/calendar/EventDetailPanel.tsx b/frontend/src/components/calendar/EventDetailPanel.tsx index f7ee3ff..0fe4b34 100644 --- a/frontend/src/components/calendar/EventDetailPanel.tsx +++ b/frontend/src/components/calendar/EventDetailPanel.tsx @@ -392,8 +392,8 @@ export default function EventDetailPanel({ // --- Handlers --- const handleEditStart = async () => { - // For shared events, acquire lock first (owners skip locking) - if (isSharedEvent && myPermission !== 'owner' && event && typeof event.id === 'number') { + // For shared events, acquire lock first + if (isSharedEvent && event && typeof event.id === 'number') { try { await acquireLock(); setLockInfo(null);